Leetcode算法学习日志-287 Find the Duplicate Number

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:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. 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; 
    }
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值