2022-11-04 10:21阅读: 31评论: 0推荐: 0

力扣-287-寻找重复数

感觉跟《剑指Offer》-50-第一个只出现一次的字符,有点神似

数组中只有一个数组会重复(可能不止两次),找出这个数字

如果用set,这是非常简单的,比两数之和还简单,所以题目要求空间复杂度O(1)

如果排个序,这题也会比较简单,只要检查有没有相邻且相等的元素,所以题目要求不能修改数组

O(n^2^)去挨个找肯定也不合适

我想到了数组下标一一对应,不过那也需要移动元素

我想到了数组和,它是个定值,等于1+2+3+…+n,拿这个和去减数组和,然后求绝对值
不行啊,这样只能得到被替换数字和重复数字的差值,条件不够

瞄了一眼题解,思路是这样,如果将数组元素的下标看作一个指针映射,那么可以把数组变成一个带环的链表,那么这题可能就变成了环形链表

能这么做也是因为题目特性,范围1-n且除重复元素外只出现一次

建立映射,从下标i=0开始,指向,同时nums[i]作为新的下标

那么直接将nums[i]作为下标,如果nums[i]==n怎么办?没有下标n
好吧,注意理解题目,如果数字数量是n,那么数组中最大的数字是n-1

听起来有点怪怪的,按理说1到1+n的数组中应该有n+1个整数才对,且最大数为n+1,但这里最大数为n,也就是说n+1被一个重复数字替代了

如果是这样,上面的映射便能够成立,不会越界

从i=0开始,nums[i]指向nums[nums[i]],以此循环

发现看不懂自己前两天写的环形链表Ⅱ的题解,而且是有问题的

首先可以确定的是,路径存在环的话,快慢指针一定会相遇,这个没有疑问,在环中满指针一定会追上
再说,当满指针第一次进入环,落在起始节点时,快指针一定已经在环中了,这也是没有问题的
这时他俩的距离一定是>=0且<环的长度的,这也是确定的
那么因为每移动一步,他俩距离就会缩小1,所以直到被追上,他俩移动的距离不会超过他俩的距离即小于环的长度大于等于0
也就是说满指针跑不完一个环,即不会跑完整个路径

那么我们知道了,满指针会在第一次遍历路径的过程中,跑不完一圈循环就会被追上
假设外段长a,循环段长b,慢指针在循环段中跑了c被追上
被追上时,慢指针走了a+c步,则快指针是他的两倍2(a+c)
同时快指针走了a+nb+c
等式nb=a+c

变形,a=(n-1)b+b-c
就是另外用一个指针从头跑a步到环的入口节点,同步已经相遇的快慢节点一起跑的话,快慢节点会在跑了b-c也就是一圈没跑完的剩余长度和n-1圈之后,和另外的指针在头节点相遇

回到本题,nums[i]>1结合上面的最大值分析,可以说这样的映射不会出现越界问题

int fast = nums[0], slow = nums[0], another = nums[0];
while (fast != slow) {
fast = nums[fast];
fast = nums[fast];
slow = nums[slow];
}

写的时候,这里的初始化有点麻烦,直接加上||fast==nums[0]当第一个节点就是目标的时候会出现死循环的情况
这里的初始化条件又恰好满足了循退出条件

int findDuplicate(vector<int>& nums) {
int fast = nums[0], slow = nums[0], another = nums[0];
while (true) {
fast = nums[fast];
fast = nums[fast];
slow = nums[slow];
if (fast == slow) break;
}
while (another != fast) {
another = nums[another];
fast = nums[fast];
}
return another;
}

改成这样,第二个循环不存在这个问题

本文作者:YaosGHC

本文链接:https://www.cnblogs.com/yaocy/p/16855616.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   YaosGHC  阅读(31)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起