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(n2).
- There is only one duplicate number in the array, but it could be repeated more than once.
给定一个数组,素组的长为n+1,里面的元素范围为1到n。其中必定会有一个元素重复了,而且可以重复很多次,需要我们找出这个重复的元素。并且限定条件为不能修改数组,假定数组是只读的;使用O(1)的空间复杂度,小于O(n^2)的时间复杂度。
刚开始只注意到了空间复杂度和时间复杂度的限制,没有注意到数组只读,所以用了之前的一种方法进行解决。就是用数组的元素作为下标去进行访问,然后将访问过的元素标记为负的,下次访问的时候观察是否为父数就能够判断是否重复了。但是这样做就修改了数组,刚开始没有注意到这个,而且ac了之后看速度快的前几个也是类似的做法,后来才注意到不能修改。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
for(int i=0;i<nums.size();i++) {
int n = abs(nums[i]);
if(nums[n] < 0) {
return abs(nums[i]);
} else {
nums[n] *= -1;
}
}
return -1;
}
};
看到Solution中的解法,其中的排序和集合都在空间或者时间复杂度上不满足限定条件,最后一个有趣的解法完全符合要求,解法与之前的链表中的142. Linked List Cycle II。最后一种类似,将数组看作成一个链表,其中能够根据数组中的元素找到下一个节点位置,所以同样的使用两个哨兵进行移动,当相遇的时候让其中一个哨兵从头开始运动,然后两个哨兵再次相遇的时候就是我们要找的重复元素,Solution中配有动画,理解起来十分方便:https://leetcode.com/problems/find-the-duplicate-number/solution/
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int tortoise = nums[0];
int hare = nums[0];
do {
//cout << tortoise << "," << hare << endl;
tortoise = nums[tortoise];//慢的走一步
hare = nums[nums[hare]];//快的走两步
} while(tortoise != hare);
int ptr1 = nums[0];
int ptr2 = tortoise;
while(ptr1 != ptr2) {
//cout << ptr1 << "," << ptr2 << endl;
ptr1 = nums[ptr1];
ptr2 = nums[ptr2];
}
return ptr1;
}
};