287.寻找重复数-图解+证明!

     

前置题目:142. 环形链表 II - 力扣(LeetCode)

寻找重复数问题要求:给定一个数组,在不修改数组且仅使用额外O(1)空间的情况下,找出数组中的重复数字。题目保证数组中一定存在重复数字。

我们可以通过建立索引与值的映射关系n->f(n)来分析数组。以数组[1,3,4,2,2]为例:

0:1
1:3
2:4
3:2
4:2

从下标0开始,以当前值作为下一个位置的索引,即可得到如下序列:0->1->3->2->4->2->4......,这形成了一个类似链表的循环结构,如下图所示:

       

        发现序列中存在环时,环中的数字对应原数组中重复数字的下标。这让人联想到142. 环形链表 II 中寻找环形入口的问题,在这里,环的入口就是重复的数字。我们可以利用快慢指针来找到这个入口,从而确定重复的数字。

        具体步骤如下:慢指针每次移动一步(slow = nums[slow]),快指针每次移动两步(fast = nums[nums[fast]])。当快慢指针第一次相遇时,说明它们已经进入环内。由于快指针的速度是慢指针的两倍,它们必定会在环内相遇,就像龟兔赛跑一样。此时,将慢指针重新指向起点(即0),然后让快慢指针都以每次一步的速度移动。当它们再次相遇时,相遇点就是环的入口,也就是我们要找的重复数字。

        证明过程如下:假设从起点到环入口的距离为F,环的长度为C。慢指针在第一次相遇时已经在环中走了a步,那么第一次相遇到环入口的距离b = C - a。假设快指针比慢指针多走了k圈,那么慢指针从起点到第一次相遇走了F + a步,快指针走了F + a + kC步。由于快指针的速度是慢指针的两倍,即2*(F + a) = F + a + kC,化简后得到F = (k - 1)*C + b。此时,慢指针指向起点,快指针位于第一次相遇的位置。两个指针每次移动一步,当慢指针走了F步到达环入口时,快指针也走了F步。由于F = (k - 1)*C + b,快指针走完(k - 1)*C步后会回到第一次相遇的位置,此时距离环入口还有b步,正好走完b步到达环入口,完成闭环。因此,第二次相遇时,慢指针指向的位置就是我们要找的重复数字。

        代码:

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

        时间复杂度:O(n),线性遍历序列

        空间复杂度:O(1) ,仅用到若干额外变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值