環形鏈表問題總結——快慢指針

環形鏈表問題是快慢指針的一個典型應用。其實,稱其爲“有環鏈表”更爲準確。

在有環鏈表中,利用快慢指針可以解決的問題包括:

判斷鏈表是否有環、求環的入口、求環的長度、求鏈表的總長度(總節點數),等。


目錄

一 判斷鏈表是否有環

二 有環鏈表的入口節點

三 有環鏈表中環的長度

3.1 方法一:第一次相遇後繼續走,直到第二次相遇

3.2 方法二:先求出環的入口節點

四 有環鏈表的總長度


具體步驟:

初始,slow和fast都從鏈表頭節點head開始,fast每次走2步,slow每次走1步。如果fast走到鏈表尾部,說明鏈表無環;否則,鏈表有環,fast和slow必定在環中的某個節點第一次相遇。【判斷是否有環get

第一次相遇後,令fast重新指向head,slow不變。二者重新開始以相同速度移動,則第二次相遇時的節點必是環的入口節點。【環的入口get】在此過程中,若記錄下fast移動的步數則是鏈表中非環部分的長度LengthLine。【非環的長度get

已知環的入口節點後,令一個指針繞環走一圈,就得到了環的長度LengthLoop。【環的長度(2) get】LengthLine + LengthLoop即爲鏈表的總長度。【鏈表總長度get

如果只求環的長度,還有另一種方法:fast和slow第一次在環中相遇後,以此作爲起點,fast仍然每次走2步,slow每次走一步。當兩個指針第二次在環中相遇時,fast恰好比slow多走了一圈。因此用fast走過的步數減去slow走過的步數,則爲環的長度。【環的長度(1) get

 

                       

 


Java代碼如下:

一 判斷鏈表是否有環

力扣:環形鏈表 I

//快慢指針
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                return true;
        }
        return false;
    }
}

二 有環鏈表的入口節點

力扣:環形鏈表 II

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        ListNode slow = pHead, fast = pHead;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                break; 
        }
 
        //無環:
        if(fast == null || fast.next == null) 
            return null;
 
        //有環:
        fast = pHead;
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

三 有環鏈表中環的長度

3.1 方法一:第一次相遇後繼續走,直到第二次相遇

    //求環的長度 (方法一:第一次相遇後繼續走,直到第二次相遇)
    public static int LengthOfLoop(ListNode head) {
        ListNode slow = head, fast = head;
        while(head != null && head.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                break;
        }
        //無環
        if(fast == null || fast.next == null)
            return -1;

        //有環
        int countSlow = 0, countFast = 0;
        while(true) {
            slow = slow.next;
            fast = fast.next.next;
            countSlow += 1;
            countFast += 2;
            if(slow == fast)
                break;
        }
        return (countFast - countSlow);
    }

3.2 方法二:先求出環的入口節點

    注:此函數的輸入爲環的入口節點

    //求環的長度(方法二:先求出環的入口節點enter)
    //... ...
    //... ...
    public static int LengthOfLoop2(ListNode enter) {
        int count = 1;
        ListNode cur = enter.next;
        while(cur != enter) {
            count++;
            cur = cur.next;
        }
        return count;
    }

四 有環鏈表的總長度

分別求出鏈表中 線性部分的長度LengthLine、環形部分的長度LengthLoop,二者相加即爲鏈表總長度。

    //求有環鏈表總長
    public static int LengthOfList(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                break;
        }
        fast = head;
        int lengthLine = 0;   //非環部分的長度
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
            lengthLine++;
        }
        System.out.println("LengthLine: " +lengthLine);
        //此時fast和slow都指向環的入口節點
        int lengthLoop = 1;  //環的長度
        fast = fast.next;
        while(fast != slow) {
            fast = fast.next;
            lengthLoop++;
        }
        System.out.println("LengthLoop: " +lengthLoop);
        return (lengthLine + lengthLoop);
    }

       如果覺得有用就點個贊吧~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章