[LeetCode] 287. Find the Duplicate Number 解题思路

时间:2021-11-24 13:11:54

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n*n).
  4. There is only one duplicate number in the array, but it could be repeated more than once.

问题:给定一个长度为 n+1 的数组,里面只有 1...n 的数值。可见必然有一个元素的值是重复出现的。假设只有最多只有一个元素的值会重复出现,求出重复的那个值。

特别要求:

  1. 不能修改原数组

  2. 只用固定空间,空间使用为 O(1)

  3. 时间复杂度要小于 O(n*2)

基于前两个要求,我首先想到的是两个指针,逐个遍历求解,但是耗时 O(n*n),超时了。在网上看到其他算法,能满足题目要求。

设 l 为左值, r 为右值, mid 为两者的中间值。值得注意的是,这里的 l, r, mid 均是指元素的值,不是指下标,之所以可以这么做,是因为题目的条件“ n+1 长的数组里面只有 1...n 的数值”。

  • 将数组扫一遍,得到大于等于 l 且 小于等于 mid 的元素个数,即为 num_l_mid。
  • 当 num_l_mid 大于 本应该有的个数,则将重复值定位在 [l, mid] 之间,缩小范围。
  • 当 num_l_mid 小于等于 本应该有的个数,则将重复值定位在 [mid, r] 之间,缩小范围。
  • 重复前面步骤,直到找到重复值。
int findDuplicate(vector<int>& nums) {

    if (nums.size() == ) {
return ;
} int l = ;
int r = (int)nums.size()-;
int mid = (r + l) / ; int cnt = ;
while (l < r) { int leftLen = mid - l + ; for (int i = ; i < nums.size(); i++) {
if ( l <= nums[i] && nums[i] <= mid) {
cnt++;
}
}if (l+ == r) {
if (cnt > leftLen) {
return l;
}else{
return r;
}
} if (cnt > leftLen) {
r = mid;
mid = (l + mid) / ;
}else{
l = mid;
mid = (mid + r) / ;
} mid = (r + l) / ;
cnt = ;
} return ;
}