首先很简单的做法即用哈希表记录数字出现的次数,最后循环哈希表输出value值不为1的key值。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
unordered_map<int, int> map;
for(int x : nums){
map[x]++;
}
for(auto& entry : map){
if(entry.second != 1){
return entry.first;
}
}
return 0;
}
};
接着就是考虑高效算法,这里可以将数组看作一个链表,这个思想确实很巧妙,但也很难考虑到。
当看作是一个链表时,其任务就是找链表的环了。
如果允许修改原数组,即我们可以标记负号表示已经来过。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int idx = 0;
while(nums[idx] > 0){
int old = idx;
idx = nums[old];
nums[old] = -nums[old];
}
return idx;
}
};
如果不允许修改原数组,则用快慢指针找环即可。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int slow = 0;
int fast = 0;
do{
slow = nums[slow];
fast = nums[nums[fast]];
}while(slow != fast);
fast = 0;
while (fast != slow) {
fast = nums[fast];
slow = nums[slow];
}
return slow;
}
};
注意,双指针解法需要从同一位置出发。
当然,此题也可用二分查找求解。
由于我们要找的数范围为[1, n],所以取mid,遍历数组,如果<=mid的数字个数小于count,则说明[1, mid]范围上有数字少了,所以重复数字在[mid, n]区间。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int len = nums.size();
int left = 0, right = len - 1;
while(left < right){
int mid = left + ((right - left) >> 1);
int count = 0;
for(int x : nums){
if(x <= mid){
count++;
}
}
if(count > mid){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
};