链表中环的入口结点

链表中环的入口结点

http://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4

#描述 这是一篇针对初学者的题解,共用两种方法解决。 知识点:单链表,哈希,双指针 难度:二星


#题解 题目抽象:给定一个单链表,如果有环,返回环的入口结点,否则,返回nullptr

##方法一:哈希法

  1. 遍历单链表的每个结点
  2. 如果当前结点地址没有出现在set中,则存入set中
  3. 否则,出现在set中,则当前结点就是环的入口结点
  4. 整个单链表遍历完,若没出现在set中,则不存在环 ###代码
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        unordered_set<listnode*> st;
        while (pHead) {
            if (st.find(pHead) == st.end()) {
                st.insert(pHead);
                pHead = pHead-&gt;next;
            }
            else {
                return pHead;
            }
        }
        return nullptr;
    }
};

时间复杂度:O(n) 空间复杂度:O(n),最坏情况下,单链表的所有结点都在存入set

##方法二:双指针法 若不用辅助结构set,该怎么做呢?这里画了一张图

  1. 初始化:快指针fast指向头结点, 慢指针slow指向头结点
  2. 让fast一次走两步, slow一次走一步,第一次相遇在C处,停止
  3. 然后让fast指向头结点,slow原地不动,让后fast,slow每次走一步,当再次相遇,就是入口结点。 如上解释:

(为了便于分析,上图所展示的是假设快指针只比慢指针多走了一圈就相遇的情况,也即快指针所走过的路程为A->B->C->D->B->C,而慢指针所走过的路程为A->B->C)

假设慢指针slow从头结点A第一次走到了环的入口结点B处时所走过的路程为X,且设环的入口结点B到达快慢指针相遇结点C处的路程为Y, 那么fast指针应该停留在D点处,且D->B的路程为Y,因为快指针fast的速度是慢指针slow的两倍且快慢指针最终同时到达相遇结点C处,
也即 D->B->C的路程 = 2*(B->C的路程),
也即D->B的路程 + B->C的路程 = 2*(B->C的路程)
化简后可得 D->B的路程 = B->C的路程 = Y

同样由于快指针的总路程是慢指针的两倍,可得 C->D->B->C的路程(也即环的周长)为X+Y,而B->C的路程上文已设为Y,也即C->D->B的路程为X。

那么在快慢指针同时到达相遇结点C处时,将快指针fast重新放到头结点A,慢指针slow的位置不变,且快指针的速度改为与慢指针slow相同,那么快指针fast从头结点A走过X路程后到达环的入口结点B,慢指针slow从快慢指针相遇节点C走过x路程后也到达了环的入口结点B,也即他们再次相遇时的节点就是环的入口结点。

###代码

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode *fast = pHead;
        ListNode *slow = pHead;
        while (fast &amp;&amp; fast-&gt;next) {
            fast = fast-&gt;next-&gt;next;
            slow = slow-&gt;next;
            if (fast == slow) break;
        }
        if (!fast || !fast-&gt;next) return nullptr;
        fast = pHead;
        while (fast != slow) {
            fast = fast-&gt;next;
            slow = slow-&gt;next;
        }
        return fast;
    }
};

时间复杂度:O(n) 空间复杂度:O(1) </listnode*>

全部评论
我跪了,解法二我想破脑壳都想不到啊
21 回复 分享
发布于 2020-07-12 17:37
解法二的通用分析。以下a为AB长度,b为BC,c为CB长度。 假设相遇时慢指针已经转了m圈,快指针已经转了n圈。可知m>=0,n>=1 因为b+c为一圈,当慢指针走了a+m(b+c)+c步长时,快指针走的步长为2a+2m(b+c)+2c 而用n表示快指针步长则为 a+b+n(b+c)。即有等式 2a+2m(b+c)+2c = a+b+n(b+c) 化简得:a = (n-2m-1)(b+c) + c 因为b+c > c,若n-2m-1小于0,则(n-2m-1)(b+c) + c < 0,即a<0 与题意不符。 所以最终得: a = (n-2m-1)(b+c) + c ,且n-2m-1>=0 若第一圈相遇即为m=0,n=1 可得a=c; 由通用公式可得,从起点出发的指针和从C点出发的指针最终会在入口处相遇。所以解法二的代码通用
11 回复 分享
发布于 2020-08-09 00:19
不 我错了 解法二是对的。
6 回复 分享
发布于 2020-07-12 11:56
解法二是对的。假设初次相遇时,快指针走了n圈,慢指针m圈,一圈长度为z,则x+y+nz=2(x+y+mz),化简后得x=(n-2m-1)z+(z-y),其中(z-y)是CDB的长度,(n-2m-1)z是绕环(n-2m-1)圈,所以两指针同速分别从pHead走、和从C开始走,一定在B点相遇。
5 回复 分享
发布于 2022-02-23 01:21
解法2表述有问题, 快指针可以经过了N次BC,而不是2次。 换言之慢指针路程为ABC时,快指针路程应该是ABC+N*圆周
4 回复 分享
发布于 2021-10-28 16:03
这个解法二有问题,这样不对。对于环前面的链表长度大于环的长度的情况的时候,这种算法就不成立。
3 回复 分享
发布于 2020-07-12 11:45
我来完整的证明一遍吧,解法二难理解的根本原因,在于题解的表述具有迷惑性。 设AB=a,BC=b,CB=c,则圆的周长 r=b+c。 第一次相遇事件:快指针fast走的路程为 a+m(b+c)+b,m为正整数。由于速度的倍数关系,得慢指针走的路程为 2[a+m(b+c)+b];单独分析慢指针,得到它的路程为 a+n(b+c)+b,n为正整数。 综上,可构建等式关系:2[a+m(b+c)=b]=a+n(b+c)+b,解得:a=(n-2m)(b+c)-b,即 a=(n-2m)r-b,即 a=(n-2m)r-b-c+c。得:a=(n-2m-1)r+c 该式子翻译成中文:a的路程=整数个的圆周+CB的路程。那么,如果让两个速度一样的新指针pre1和pre2,一个从A走,一个从C走,当pre1走完a,到达B点时,pre2走的路程是:整数个圆周+CB,正好也到达了B点。所以二者正好相遇。 题解难以理解的根本原因在于:活用了第一次相遇问题的快慢指针,让他们在第二次相遇问题中继续使用,增加了读者的理解难度,实际上,正常人的解题思路是,根据双指针思路,设计第一次相遇问题,从第一次相遇问题得出等式,根据等式的特点继续设计第二次相遇问题,进而得解。
3 回复 分享
发布于 2022-03-01 19:21
哇,解方程呢?
1 回复 分享
发布于 2020-08-28 17:03
官方解答真不严谨。。。。
1 回复 分享
发布于 2020-12-21 16:30
意思是第二次必然能在C点再次相遇,因为速度相同,所以首次相遇必在交点
1 回复 分享
发布于 2021-07-07 11:33
其实当前段大于环长度的情况可以将环扩大到×n的长度,是可以套用官方的方程来解的,本质一样,扩大环倍数后仍然会相遇,只不过将n个环的周长合并为一个大圆来满足方程。
1 回复 分享
发布于 2021-08-17 11:17
解法一代码写错了 if (st.find(pHead) == st.end()) { st.insert(pHead); pHead = pHead->next; }
1 回复 分享
发布于 2021-11-26 00:33
增加用例后解法2不行了
点赞 回复 分享
发布于 2021-10-27 10:19
当直的部分X大于圆环的长度时,可能快指针会多转几圈,是n乘圆周-y等于x,所以后面求交点时慢指针会在圆环里转n-1圈然后两个节点相交在入口
点赞 回复 分享
发布于 2021-11-25 21:45
解法二的解释里有些问题,快指针有可能已经绕了n圈。所以时间复杂度会高一些。但是不影响正确性。
点赞 回复 分享
发布于 2021-12-29 11:55
解法二的思路太强了
点赞 回复 分享
发布于 2022-01-12 19:47
解法二判断有环还算简单,判断入口那个能找到这个规律是真的强
点赞 回复 分享
发布于 2022-01-26 21:54
没看懂。。
点赞 回复 分享
发布于 2022-02-14 20:59
如果快指针是走了两个环甚至是更多环后才与慢指针第一次相遇呢?没想明白
点赞 回复 分享
发布于 2022-02-14 22:39
首次相遇快慢指针走了路: 快指针:AB + 快指针n圈 + BC 慢指针:2*(AB * 慢指针m圈 + BC) 这个怎么推出 CDB = AB = x ?
点赞 回复 分享
发布于 2022-02-14 22:43

相关推荐

字节 飞书绩效团队 (n+2) * 15 + 1k * 12 + 1w
点赞 评论 收藏
分享
评论
113
19
分享
牛客网
牛客企业服务