求单链表中环的起点,原理详解


1. 问题描述:

链表结构如下,若链表中有环,返回环的起点,否则返回NULL

1 struct ListNode
2 {
3     int val;
4     ListNode *next;
5     ListNode(const int val):val(val),next(NULL) {}
6 };

 2. 解题代码:

参考 LeetCode

 1 ListNode *detectCycle2(ListNode *head) {
 2     if(!head) return head;
 3     ListNode *stepA = head;// 每次走一步
 4     ListNode *stepB = head;// 每次走两步
 5     while (stepB && stepB->next) {
 6         stepA = stepA->next;
 7         stepB = stepB->next->next;
 8         if(stepA==stepB){
 9             ListNode *stepC = head;
10             while (stepC != stepA) {
11                 stepC = stepC->next;
12                 stepA = stepA->next;
13             }
14             return stepC;
15         }
16     }
17     return NULL;
18 }

3. 原理详解:

(1)假设链表头到环起点的距离为S1,环的长度为S0;在环中相遇时stepA走的路程是SA,stepB走的路程是SB。则当SB>=SA>=S1时,以下结论成立:

(SA-S1) mod S0 = (SB-S1) mod S0    <=>   (SB-SA) mod S0 = 0   <=>   SB-SA = n*S0   <=>   SA=n*S0;  (SB>=SA>=S1)

a. 因为stepB的速度是stepA速度的两倍,所以SB=2SA;

b. 所以 SB-SA=SA=nS0>=S1;

c. 所以 n>=S1/S0,n为自然数

d. 结论:只要满足条件 n>=S1/S0,则stepA和stepB就会在环上相遇;

e. 第一次相遇时 n 的值为 S1/S0 向上取整,假设此时 n=n1,则 SA=n1*S0

(2)stepA和stepB第一次在环上相遇后,让stepB停下,stepC从表头开始走,stepA从当前位置走,并且stepA和stepC一次只走一步。

 假设在环上相遇时stepA走的路程是SAA,stepC走的路程是SC,则当SC>=S1时,以下结论成立:

(SC-S1) mod S0 = (SAA-S1) mod S0    <=>   (SAA-SC) mod S0 = 0     <=>   (SC+n1*S0-SC) mod S0 = 0     <=>   (n1*S0) mod S0 = 0;  (SC>=S1)

a. 因为stepA和stepC的速度相同,所以等式SA A= SC+n1*S0成立;

b. 所以(SAA-SC) mod S0 = 0     <=>   (SC+n1*S0-SC) mod S0 = 0     <=>   (n1*S0) mod S0 = 0

c. 因为 (n1*S0) mod S0 = 0 恒成立,所以 stepA和stepC在环上相遇也恒成立,只要满足SC>=S1就行;

d. stepA和stepC在环上第一次相遇时,SC=S1,此时stepC正好在环的起点。