【小白也能懂】链表中环的入口结点 怎么搞这么麻烦!看我解法 小白也能懂!

链表中环的入口结点

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

写在前面:
这份解法是搬运,图片也是原回答评论区网友贡献,非本人原创:https://leetcode.com/problems/linked-list-cycle-ii/discuss/44774/Java-O(1)-space-solution-with-detailed-explanation.
请大家支持原回答,如果可以去点赞或者感谢的请去原回答表示谢意。

题目:
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

  1. 这题我们可以采用双指针解法,一快一慢指针。快指针每次跑两个element,慢指针每次跑一个。如果存在一个圈,总有一天,快指针是能追上慢指针的。
  2. 如下图所示,我们先找到快慢指针相遇的点,p。我们再假设,环的入口在点q,从头节点到点q距离为A,q p两点间距离为B,p q两点间距离为C。
  3. 因为快指针是慢指针的两倍速,且他们在p点相遇,则我们可以得到等式 2(A+B) = A+B+C+B. (感谢评论区大佬们的改正,此处应为:如果环前面的链表很长,而环短,那么快指针进入环以后可能转了好几圈(假设为n圈)才和慢指针相遇。但无论如何,慢指针在进入环的第一圈的时候就会和快的相遇。等式应更正为 2(A+B)= A+ nB + (n-1)C)
  4. 由3的等式,我们可得,C = A。
  5. 这时,因为我们的slow指针已经在p,我们可以新建一个另外的指针,slow2,让他从头节点开始走,每次只走下一个,原slow指针继续保持原来的走法,和slow2同样,每次只走下一个。
  6. 我们期待着slow2和原slow指针的相遇,因为我们知道A=C,所以当他们相遇的点,一定是q了。
  7. 我们返回slow2或者slow任意一个节点即可,因为此刻他们指向的是同一个节点,即环的起始点,q。

图片说明

具体代码如下:

public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null || pHead.next == null){
            return null;
        }

        ListNode fast = pHead;
        ListNode slow = pHead;

        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                ListNode slow2 = pHead;
                while(slow2 != slow){
                    slow2 = slow2.next;
                    slow = slow.next;
                }
                return slow2;
            }
        }
        return null;

    }
}
全部评论
第三步有问题吧,快指针进入环之后不一定就只转了一圈就与慢指针相遇了,应该是慢指针进入环之后没走慢一圈就会与快指针相遇,而此时应设快指针走了N圈,按照楼主设定的变量等式应该为2(A+B) = A+N*B+(N-1)C; 具体解答参照https://cyc2018.github.io/CS-Notes/#/notes/23.%20%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%8E%AF%E7%9A%84%E5%85%A5%E5%8F%A3%E7%BB%93%E7%82%B9; 不知道是不是我理解错了,欢迎讨论哈
9 回复 分享
发布于 2020-02-27 12:01
如果环前面的链表很长,而环短,那么快指针进入环以后可能转了好几圈才和慢指针相遇。但无论如何,慢指针在进入环的第一圈的时候就会和快的相遇。假设环长是s,(a+b)*2=a+n*s+b 可以得出a+b=n*s 。 现在新建一个slow2的指针指向头结点,原来的slow指针依旧停留在相遇点p。接下来他们都以一次走一个位置的速度往前走。为什么当slow2走完a路程到入口节点处会和slow指针相遇呢? 因为通过a+b=n*s,可以推出a= n*s-b 。它正好对应slow指针走的路径长度。
1 回复 分享
发布于 2020-05-16 11:59
对不起,我不配作为小白~
1 回复 分享
发布于 2022-03-16 18:01
快指针走了n(n >= 1)圈后与慢指针相遇, 固有: 2(A + B) = n(B+C)+B+A A = nC+(n-1)B A = C + (n-1)(C+B) 因为C+B为一圈的长度,所以用c, h两个指针从p点和head开始走,当h走完A时,c走过的路为C + (n-1)(C+B),即n圈+C,所以h和c的相遇点为q
25 回复 分享
发布于 2020-04-16 00:52
直接拿路程列等式怪怪的,应该加入时间t和速度v。t时间内慢指针走过的路程为A+B,快指针走过的路程为A+B+n(B+C),由快指针是慢指针速度的两倍关系得2(A+B)/t=(A+B+n(B+C))/t,则2(A+B)=A+B+n(B+C),即A=n(B+C)-B。由外层while先找到快慢指针首次相遇点p点,即将slow定位到p点,新增内层while的慢指针slow2从头节点出发,同时slow也从p点开始转圈,slow2走完A路程时,因为A=n(B+C)-B,所以slow走过n(B+C)-B即n圈减掉再减掉B长度,因此slow和slow2相遇点为入口点q
1 回复 分享
发布于 03-08 16:59 广东
你这fast跟slow是不是写反了啊,第13行、14行
点赞 回复 分享
发布于 2020-03-05 17:21
ListNode* EntryNodeOfLoop(ListNode* pHead) { ListNode* r=pHead; ListNode* next=pHead->next; while(next) { if(next <r>next; next=next->next; } } return NULL; } 为什么我的很简单复杂度O(n)</r>
点赞 回复 分享
发布于 2020-04-22 12:39
3修改之后,4应该改成a=(n-2)(b+c)+c 到6之后,新指针和慢指针一样速度,则在慢指针走了n圈(长度b+c)后,就能到博主的这种情况了
点赞 回复 分享
发布于 2021-04-28 10:31
妙啊👍
点赞 回复 分享
发布于 2021-07-08 13:21
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) { } }; */ class Solution { public: ListNode* EntryNodeOfLoop(ListNode* pHead) { ListNode *pre = pHead, *temp; while(pre){ if(pre->next==pre) return pre->next; temp = pre; pre = pre->next; temp->next = temp; } return NULL; } };
点赞 回复 分享
发布于 2021-10-08 17:08
感觉上面的公式还是不正确,正确的公式应该为:A + (n-1)(B+C) + B = 2(A + (n/2)(B+C)) 前半部分为fast走过的路程计算,后半部分为slow走过的路程计算。n表示在环内相遇的时候,fast走过的圆圈数量。
点赞 回复 分享
发布于 2022-04-22 18:09
分析严谨,好文
点赞 回复 分享
发布于 08-03 18:50 广东

相关推荐

评论
149
5
分享
牛客网
牛客企业服务