1. 程式人生 > 其它 >20200612-判斷連結串列有環

20200612-判斷連結串列有環

技術標籤:AlgorithmARTS

1.1 判斷連結串列有環

這2道題都和判斷連結串列是否有環相關,看下給的例子

Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.

list cycle

對於這樣一個帶環的連結串列,判斷是否有環,最簡單的做法就是遍歷所有的節點,如果遇到重複的節點,則說明有環,由此解法便是,用一個 visitedSet

來裝已遍歷過的節點,當遇到重複的節點,則說明有環,第一次遍歷到的重複節點便是環的入口,這個解法需要額外提供一個 Set 空間,若是要求使用 O(1)空間複雜度,就有了另外一個思路:

使用一個快指標和一個慢指標來遍歷 list, 如果連結串列有環,則必然存在在某一個節點上2個指標相遇。

public boolean hasCycle(ListNode head) {
        	if (head == null || head.next == null) {
                return false;
            }
            ListNode slow =
head; ListNode fast = head.next; while (slow != fast) { if (fast == null || fast.next == null) { return false; } slow = slow.next; fast = fast.next.next; } return true
; }

如果要找出環的入口位置呢?這裡就涉及一點點數學的上換算了:

假設連結串列 head 到環入口的節點數為 a(不包含入口節點),環的長度為b, 慢指標走過的路程為 s,快指標走過的路程為f, 當2個指標第一次相遇時,則有如下關係:

  • 第一次相遇:
  1. f = 2s (快指標每次走2步,慢指標每次走1步,所以相遇時路程是 2倍)
  2. f = s + k*b

這個公式就不太好理解, 假設相遇時的節點距離入口的距離為d, Slow 繞圈為 m, Fast 繞圈為 n, 則:

  • s = a + d + m*b;
  • f = a + d + n*b;

換算一下就得出,f = (n-m)*b + s; (n>m, 快指標繞圈更多),即 f = s + k * b;

我們將1,2 消一下就得到,s = k * b, 所以說慢指標走了環的 k 圈,若此刻有一個節點從 head 處開始走,它每次路過環入口的時機應該在 a + k * b(k 為繞圈數),由於慢指標 s=k * b, 所以讓一個節點從頭開始走,當它們相遇時,即為環的入口了。

1.2 反轉連結串列

還有一類經典的連結串列題是反轉連結串列,自己做題的過程中發現要注意的點:

  • 如何準確的處理節點之間的關係,先連結後面的關係(先使節點有多個父節點),再修改節點連結
  • 知道哪個是 head 節點,比如全部反轉的 list 來說,最後一個節點是新的Head, 對 swap nodes in pairs來說新的 head 是第2個節點,對reverse nodes in k-gourp來說,新的 head 是第一組的最後一個節點
  1. 那麼對於全反轉來說 prev 走到最後一個節點就是新的 Head
ListNode prev=null;
ListNode cur=head;
while(cur != null){
    ListNode next = cur.next;
    cur.next = prev;
    prev = cur;
    cur = next;
}
return prev;
  1. 對於不是最後指標的位置是新 head的,都使用 dummy 來連結新的 Head
ListNode dummy= new ListNode(-1);
dummy.next = head;

mmy` 來連結新的 Head

ListNode dummy= new ListNode(-1);
dummy.next = head;

當 reverse 完畢後, dummy.next指向新的 head.