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正好在環的起點。