287. Find the Duplicate Number(链表判环)
来源:互联网 发布:郑州淘宝加盟被骗了 编辑:程序博客网 时间:2024/05/16 17:34
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.
首先讲一下我的思路:
1.哈希表
对于这种数组中找重复元素的问题,我当然第一时间想到哈希表,但是哈希表空间复杂度是O(n),所以不能用。
2.暴力法
两个循环就可以暴力查找出重复的元素,时间复杂度是O(n^2),题目要求less than O(n^2),所以排除。
4.排序
时间是可以,但会改变数组,所以不可取。
5.二分法
我想到了二分法,但是刚开始我思路走入了误区。一共n+1个元素,每个元素都是在[1..n]范围之内,那么除过一个重复的,剩下的肯定就是无重复的且范围在[1..n]之内的元素了。
况且,这道题二分法和一般的不一样,这道题的思想是这样的。既然有元素值的范围,我们对元素值的范围进行二分,而不是对数组二分,然后通过统计数组中元素个数来加上限制条件,缩小元素值范围继续二分下去。
比如:对于元素集合[1..10],现在有n+1个元素,即10+1=11个,那么必定有一个重复元素。
我们首先将元素集合分为[1..5] 和 [6..10],此刻mid=5。通常来说,如果遍历数组得到<=5的元素数目和mid相等,则说明重复元素在右半区间中。否则,在左半区间中。然后继续二分下去,直到最终只有一个元素。例如:元素4重复了,那么数组中会有1,2,3,4,4,5,一共6个元素,这不正说明重复元素在mid即5的左半区间吗?
由于一直二分下去,最终剩下两个元素的时候,high会向low靠拢,所以我们返回low即可。
代码如下:
class Solution {public: int findDuplicate(vector<int>& nums) { size_t size = nums.size(); if(size ==0 || size == 1) return -1; return binsearch_like(nums, size, 1, size-1); } private: int binsearch_like(vector<int>& nums, int size, int low, int high){ while(low < high){ int mid = low + (int)((high-low)>>1); int count = 0; for(int i=0; i<size; ++i){ if(nums[i] <= mid) ++count; } if(count > mid) high = mid; else low = mid+1; } return low; }};
4.映射找环法
对于数组a[3] = {1, 2, 3},我们把它看作下标和元素值的映射。0->1, 1->2, 2->3, 连起来就是 0->1->2->3。
既然存在重复元素,例如数组a[5] = {1, 2, 3, 3, 4}, 连起来就是 0->1->2->3->3->3...无限环转圈
或者数组a[6] = {1, 2, 4, 5, 3, 3},连起来就是0->1->2->4->3->5->3->5->3->5->3....同样无线循环转圈
这道题这就转换成了链表求环的问题。
这种题的通常解法就是一个快指针,一个慢指针。快指针步长为满指针的2倍。就好像在操场跑步,一个人跑的快,一个人跑的慢,只要操场是个环,那么跑的快的总会追上跑得慢的。追上的点就是有环点。
由于步长的
然后再定义一个指针从0开始,之前的指针从有环点开始,两个相遇点即为重复元素所在。
代码如下:
class Solution {public: int findDuplicate(vector<int>& nums) { size_t size = nums.size(); if(size ==0 || size == 1) return -1; 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 slow; } };
证明如下:
如果fast和slow相遇,那么在相遇时,slow肯定没有遍历完链表,而fast已经在环内循环了n圈(n>=1)(见下面的证明2)。假设 slow走了s步,则fast走了2s步(fast的步数还等于s加上在环上多转的n圈),设环长为r,则:
2s=s+nr
s=nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a,则
a+x=s=nr
a+x=(n-1)r+r=(n-1)r+L-a
a=(n-1)r+(L-a-x)
(L-a-x)为相遇点到环入口点的距离。由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点。于是可以从链表头和相遇点分别设一个 指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
- 287. Find the Duplicate Number(链表判环)
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287.Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 字符串排序
- openvswitch创建vxlan隧道和gre隧道的mtu问题
- vim-操作命令
- 找老乡
- 简单字符串排序
- 287. Find the Duplicate Number(链表判环)
- 小鑫の日常系列故事(二)——石头剪子布
- 真正从零开始,TensorFlow详细安装入门图文教程!
- 小鑫の日常系列故事(十)——排名次
- VS2012中使用Link.exe手动链接obj
- 图解js原型(原型,对象,函数之间的关系)
- LeetCode No.219 Contains Duplicate II
- spark sql
- [Java1.8]_[Stream]