https://github.com/dolphinmind/datastructure/tree/datastructure-linkedlist
分析
證明過程
基本假設
- 假設環的長度為(C)
- 假設從連結串列的頭部到環的入口點的距離為(A)
- 假設從環的入口點到快慢指標第一次相遇點的距離為(B)
- 假設從快慢指標第一次相遇點回到環的入口點的距離為(C-B)
證明過程
- 慢指標的移動
- 慢指標每次移動一步
- 當慢指標進入環後,它會沿著環移動,直到與快指標相遇
- 快指標的移動
- 快指標每次移動兩步
- 當快指標進入環後,它也會沿著環移動,直到與慢指標相遇
- 相遇點分析
- 當快指標和慢指標相遇時,假設慢指標走了(x)步,那麼快指標走了(2x)步
- 慢指標從連結串列頭部到相遇點的距離為(A+B+rC),其中(r)是慢指標在環內繞圈的次數
- 快指標從連結串列頭部到相遇點的距離為(A+B+kC),其中(k)是快指標在環內繞圈的次數
- 因為快指標的速度是慢指標的兩倍,所以快指標走的距離是慢指標的兩倍,即2(A+B+rC) = A+B+kC
- 求解方程
- 從上述方程可以得出A + B + rC = |k-r|C => A + B = C + hC
- 這意味著慢指標走過的距離 (A+B+rC)是環長度(C)的整數倍
- 由於 (A+B+rC)是慢指標從連結串列頭部到相遇點的距離,而(|k-r|C)是快指標在環內繞圈的總長度,這意味著快指標和慢指標在環內相遇
這時可以發現快指標離環入口只需要走(C-B) + hC 距離,與連結串列的頭部到環入口距離一致,將快指標回填到連結串列頭部,類似於尾對齊發,即找到了相交點
主類
package com.github.dolphinmind.linkedlist;
import com.github.dolphinmind.linkedlist.uitls.ListNode;
/**
* @author dolphinmind
* @ClassName LinkedListCycle
* @description 141. 環形連結串列
* @date 2024/8/4 slow的速度v = 1
* fast的速度v = 2
* 在沒有環的情況下,fast會最先到達末尾,從而返回false
* 在有環的情況下, fast在未進入環之前的情況和上述類似,fast進入環之後,fast開始在環內轉圈
* 直到slow也進入圈內,兩者的相對速度為v = 1,此時fast指標反追擊slow,直到fast追擊到slow為止
* 剛剛在思考的時候,突然想起連結串列相交的問題:發覺160連結串列相交問題與當前141環形連結串列問題有一定的相似性
*
* 假設 headA = 1->2->3->4
* headB = 5->6->7->8->9->4
*
* slow = 1->2->3->4->5->6->7->8->9->4
* fast = 5->6->7->8->9->4->1->2->3->4
*
* head = 1->2->3->4->5->6->7->8->4
* slow = 1->2->3->4->5->6->7->8->4->5->6
* fast = 1->3->5->7->4->6->8->5->7->4->6
*/
public class LinkedListCycle {
/**
* 雙指標法
* @param head
* @return
*/
public boolean hasCycleTowPointer(ListNode<?> head) {
if (head == null || head.next == null) {
return false;
}
ListNode<?> slow = head;
ListNode<?> fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
return true;
}
}
return false;
}
}