如果不将思考题考虑在内的话,那么AcWing 13.找出数组中重复的数字中的方法1的代码是可以完全移植过来的。这里不再赘述。不过,由于这道题的样例里面不存在含有超出范围的数据以及全部数据均出现一次的情况,因此代码还可以进一步简化。
简化后的具体代码如下:
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int n=nums.size();
vector<int> v(n, 0);
//统计出现次数
for(auto &e: nums){
v[e]++;
}
//遍历统计结果,发现有满足条件的则返回
for(int i = 0; i < n; i++){
if(v[i] > 1) return i;
}
return 0;
}
};
现在,我们来解决思考题。由于要求空间复杂度为O(1),因此不可以再开辟等量空间统计出现次数。
为了解决问题,我们先按照元素所在的区间划分为等长的子区间(注意,这里的区间并不是数组的区间,而是数组中元素取值范围的区间)。然后再遍历数组统计落在这两个子区间里面元素的个数。假设所有的元素均是互异的,那么落在两个子区间里面元素的个数应该恰好等于区间长度。当存在重复元素时,两个子区间中应至少有一个是统计个数大于区间长度的。此时,只要在出现这种情况的子区间中继续如上的操作,最终就能得到重复的元素。该算法原理类似二分查找,不过要注意二分查找的子区间是按照数组划分的。
具体代码如下:
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int lo = 1, hi = nums.size() - 1;
while(lo <= hi){
int sum = 0; //统计数组元素在左子区间范围中元素个数
int mid = lo + (hi - lo) / 2; //将区间划分为[lo, mid]和[mid + 1, hi]
for(auto e: nums){
if(lo <= e && e <= mid) sum++;
}
if(sum <= (mid - lo + 1)) lo = mid + 1;
else hi = mid - 1;
}
return lo;
}
};