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:
- You must not modify the array (assume the array is read only).
- You must use only constant, O(1) extra space.
- Your runtime complexity should be less than
O(n*n)
. - 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 ;
}