leetcode 287. Find the Duplicate Number

本文介绍了一种使用Floyd's Algorithm来检测数组中是否存在重复元素的方法,并详细解析了如何利用该算法找到重复元素的具体位置。通过将数组视作一个可能包含环的链表,我们能够有效地解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考博客:https://segmentfault.com/a/1190000003817671

 里面介绍很多方法,而且都给出空间和时间复杂度。

 选用最后一种方法,学到新算法。Floyd's Algorithm

 把整个数组看成是数组下标到数组元素的映射,如果有重复元素的话,由映射关系形成

 的链表会形成环。如引用原博客的解释:

 假设数组中没有重复,那我们可以做到这么一点,就是将数组的下标和1n每一个数一对一的映射起来。

 比如数组是213,则映射关系为0->2, 1->1, 2->3。假设这个一对一映射关系是一个函数f(n),其中n是下标,

 f(n)是映射到的数。如果我们从下标为0出发,根据这个函数计算出一个值,以这个值为新的下标,再用这个函数计算,

 以此类推,直到下标超界。实际上可以产生一个类似链表一样的序列。比如在这个例子中有两个下标的序列,0->2->3

 但如果有重复的话,这中间就会产生多对一的映射,比如数组2131,则映射关系为0->2, {13}->1, 2->3

 这样,我们推演的序列就一定会有环路了,这里下标的序列是0->2->3->1->1->1->1->...,而环的起点就是重复的数。

 所以判断是否有重复元素就可以转换成判断链表是否形成环,寻找数组里面的重复元素(只有一个)可以转换成

 查找链表中环的起点。

 可以用Floyd's Algorithm的龟兔赛跑解答:

 首先设环的起点是m,环的长度是l,乌龟和兔子第一次相遇的地点到环起点的距离是k

 乌龟和兔子都是从链表的起点出发,兔子的速度是乌龟的两倍,如果链表存在环,很容易想到,兔子和乌龟总会在环里的

 某个节点相遇,这时候

 乌龟移动了i步,存在关系i = m + a * l + k  (a是乌龟绕着环跑的圈数)

 兔子移动了2i步,存在关系2i = m + b * l + k  (b是兔子绕着环跑的圈数)

 根据这两个关系可以得到关系:m + k = (b - 2a) * l

 根据这一个关系可以设计第二轮赛跑,第二轮赛跑乌龟和兔子的速度相等,但是起点不同,乌龟的起点是链表的起点,

 兔子的起点是m+k,当它们再次相遇的时候,这个节点就是环的起点。

 对第二轮的赛跑的解释:

 兔子的起点是m + k,乌龟从链表的起点到环的起点共走了m步,那么兔子也走了m步,根据关系m + k = (b - 2a) * l

 兔子再走k步就能绕环转整数倍,所以兔子此时的位置是在它的起点往后倒退k步,所以兔子的终点就是m,即环的起点。


class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int tortoise = nums[0], hare = nums[nums[0]];
        while (tortoise != hare) {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        }
        //tortoise = nums[0];
        //hare = nums[hare];
        tortoise = 0; //虽然这里不存在环的起点的case,但是还是这样写比较好
        while (tortoise != hare) {
            tortoise = nums[tortoise];
            hare = nums[hare];
        }
        return tortoise;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值