您的位置:首页 > 其它

[Leetcode]287. Find the Duplicate Number简单解法及解释 双指针复杂度O(n)

2016-01-01 11:34 323 查看
题目链接:Find the Duplicate Number

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,时间复杂度为n。

先上代码

class Solution {
public:
int findDuplicate(vector<int>& nums) {
if (nums.size() > 1){
int slow = nums[0];
int fast = nums[nums[0]];
while (slow != fast)
{
slow = nums[slow];
fast = nums[nums[fast]];
}

fast = 0;
while (fast != slow)
{
fast = nums[fast];
slow = nums[slow];
}
return slow;
}
return -1;
}
};


基本思想是将数组抽象为一条线和一个圆环,因为1~n 之间有n+1个数,所以一定有重复数字出现,所以重复的数字即是圆环与线的交汇点。然后设置两个指针,一个快指针一次走两步,一个慢指针一次走一步。当两个指针第一次相遇时,令快指针回到原点(0)且也变成一次走一步,慢指针则继续前进,再次回合时即是线与圆环的交汇点。

看到这里是否感到似曾相识,没错!这就是Linked List Circle2的变形题目!参见我的另一博文点我跳转

看到这里有的童鞋可能已经晕了,为什么一定会汇合?为什么汇合点就是重复数字?不要着急,我们先把Linked list circle问题弄明白。如图1所示,两个指针同时从直线起点开始,假设在x处第一次汇合,xo之间距离为x,那么快指针走过的路程为a+c+x,慢指针走过的路程为a+x,所以a+c+x=2(a+x),所以c=a+x,也就是SO之间的距离等于xo,所以令快指针从起点开始一次一步,慢指针从x开始,同时前进,则必会在O处相遇!



明白了上面的原型后,我们来看这个变型。把数组抽象成线和圆环,举例来说,假设我们有一个数组是nums[]=[1,2,3,4,5,5,6,7],pf代表快指针,ps代表慢指针,初始ps指向nums[0],即1,pf指向nums[nums[0]],即2,行动一次后,ps指向nums[1],即2,pf指向nums[nums[2]],即4,再动一次,ps指向nums[2],即3,pf则指向了nums[nums[4]],即5;可以发现pf一旦指向5后便不会再动,因为nums[5]一直为5,直到ps慢慢追上,然后令pf从头开始,ps一直在5处停留,最后定会相遇在这里,而这里就是重复数字。这里举了个最简单的例子,是为了方便大家理解,实际上实际的圆环顺序与数组的顺序是没有关系的,不信可以自己在纸上画一画,当数组变成nums[]=[4,6,5,1,3,2,5,7]的样子,你会更加理解这个算法的!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息