Leetcode 287 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.
题意分析
给定n+1个数,它们的取值从1-n选取,可以证明一定会有重复元素,如果限定只有一个重复元素,找出这个重复元素。
解法分析
本题是双指针(快慢指针)的应用,解法同Leetcode 142 Linked List cycle II,现在先来讲解下142.该题给定一个链表,要求如果链表有圈,则找出这个圈的入口点,如果没有,则返回null指针。情形如下图所示:
令快指针慢指针分别为fp和sp,这里都令快指针走两步,慢指针走一步,如果不是两倍的比例,可能会造成不便。两个指针同时从h出发,如果fp->next或fp->next-next为空,则无圈,否则当sp进入圈中时,fp可以看做按顺时针追及sp,由于每一次运动它们的间距减一,而sp刚进圈时它们的间距小于圈长r,因此fp必然在sp走完一圈之前追上sp,此时fp已经绕圈n次。该模型能解决如下三个问题:
- 是否有圈
当fp->next==NULL或fp->next->next==NULL时无圈,第二个判断主要是避免链长为2的情形。
- 圈长
如果确定有圈,则当fp==sp时它们相遇,此时只需要让一个指针不动,另一个指针按1的步长运动,等到再次相遇时的步数就是圈长r。
- 入圈点
如上图,当第一次相遇时,sp走了a+x,fp走了a+x+nr,由于它们的速度是两倍关系,所以2(a+x)=a+x+nr,a+x=nr,a=(n-1)r+r-x,r-x为相遇点到入圈点的距离,此表达式表示如果给定两点,一个从h(头)出发,一个从相遇点c出发,它们一定能在入圈点相遇,因此找到相遇点后,只需要进行上述操作就能找到入圈点。C++代码如下:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast=head;
ListNode *slow=head;
if(head==NULL)
return head;
while(1){
if(fast->next==NULL||fast->next->next==NULL)
return NULL;
fast=(fast->next)->next;
slow=slow->next;
if(slow==fast)
break;
}
slow=head;
while(slow!=fast){
slow=slow->next;
fast=fast->next;
}
return fast;
}
};
对于本题,数组的下标就是指向它的指针,而它值就是他的next指针,如下例:
下标:0 1 2 3 4 5
值: 1 3 2 4 5 4
可以看出head指针为0,它的元素为1,下标1指向元素3,下标3指向元素4,下标4指向元素5,下标5指向元素4,下标4又指向元素5,可以看到从元素4开始进入圈。因此定义fast和slow作为快慢指针(下标),用上述方法即可得到结果。C++代码如下:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int fast=0;
int slow=0;
while(1){
fast=nums[nums[fast]];
slow=nums[slow];
if(fast==slow)
break;
}
fast=0;//slow is alse ok
while(fast!=slow){
fast=nums[fast];
slow=nums[slow];
}
return fast;
}
};