leetcode - Find the Duplicate Numbe
链接:https://leetcode.com/problems/find-the-duplicate-number/
两种解法:
1)利用二分+鸽巢原理
其实就是使用二分搜索来枚举重复数字的范围;根据鸽巢原理,如果数组中<=1/2n的数字超过了1/2n,则说明1~1/2n中有重复的数字,因此我们可以根据该原理来枚举重复数字所在的范围,具体实现代码如下:
class Solution { public: int findDuplicate(vector<int>& nums) { int n = nums.size(); int l = 1; int r = n - 1; while(l <= r) { int m = l + ((r - l) >> 1); int cnt = 0; for(int i=0; i<n; ++i) { if(nums[i] <= m) { ++cnt; } } if(cnt > m) { r = m - 1; } else { l = m + 1; } } return l; } };
上述算法的时间复杂度是O(nlogn)
2)类似链表找环的起始节点(映射找环法)
首先来看一下具体的一个例子,数组元素为2,1,3,将数组下标和元素构成一个映射,即0->2, 1->1, 2->3,我们将该映射看成是一个函数f(n),其中n是元素下标,f(n)是该下标对应的元素;如果我们以0为下标,根据f(n)计算出一个值,再以这个值为下标,则可以得到类似链表的一串数字,0->2->3
如果数组中有重复的数字,则在上述串会出现重复,例如数组元素为2,1,3,1,则进行顺序映射过程中,有0->2->3->1->1->1.....,因为我们可以借鉴类似链表找环的方式,来查找重复的数字,并且重复的数字就是环的起始位置
具体代码如下:
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); int find = 0; while(find != slow) { find = nums[find]; slow = nums[slow]; } return find; } };
上述算法的时间复杂度是O(n),可以参加https://segmentfault.com/a/1190000003817671