1. 程式人生 > 其它 >JZ23 連結串列中環的入口結點

JZ23 連結串列中環的入口結點

給一個長度為n連結串列,若其中包含環,請找出該連結串列的環的入口結點,否則,返回null。

JZ23 連結串列中環的入口結點

描述

給一個長度為n連結串列,若其中包含環,請找出該連結串列的環的入口結點,否則,返回null。

資料範圍:n ≤ 1000;

要求:空間複雜度O(1),時間複雜度O(n)。

示例1

輸入:

{1,2},{3,4,5}

返回值:

3

說明:返回環形連結串列入口節點,我們後臺會列印該環形連結串列入口節點,即3。

示例2

輸入:

{1},{}

返回值:

"null"

說明:沒有環,返回null,後臺列印"null"

解析

這題初見是真的毫無思路,無從下手,在看了題解後才恍然大悟,所以通過這篇筆記記錄一下思路。
要找到連結串列中環的入口節點,需要經過三步:判斷連結串列是否有環、求出環的長度、找到環的入口

判斷連結串列是否有環

先判斷連結串列中是否有環,若有環則進行後面的操作,若無環則直接返回 null。此處我們可以採取設定快慢兩個指標的方式來進行這個判斷。快慢指標都從頭開始走,快指標一次走兩步,慢指標一次走一步,若有環,則兩指標必相遇(追及問題),若無環,則快指標會先走到空。

求出環的長度

這一步是為後面進行鋪墊,到這步說明連結串列中存在環,且此時快慢指標在環中的相同位置(追上了)。故此時讓慢指標繼續一步步走,每走一步計數值加一,再次遇到快指標時停止,此時的計數值就是環的節點數,即環的長度。

找到環的入口

有了前面的資料,就能找到環的入口了,這是非常巧妙的一步。
首先,將快慢指標重置到連結串列頭部,讓快指標先走N步,N為連結串列環的長度,然後再讓慢指標和快指標一起一步步走,當兩指標相遇時,所在之處就是環的入口。

原理分析:設連結串列除環之外的長度為X,則連結串列的實際節點數應為X+N,第X+1個節點就是環的入口,且該節點與第X+1+N個節點是同一個節點(相當於從入口處走了一個環的長度);快指標先走了N步,處於第1+N個節點,此時慢指標處於第1個節點,這時候它們同步開始走,經過X個節點後,快指標處於第1+N+X個節點,慢指標處於第1+X個節點,即它們在同一個位置,環的入口。

程式碼清單

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead) {
        // 1.判斷連結串列中有無環:快慢指標相遇
        ListNode slow = pHead;
        ListNode fast = pHead;
        boolean flag = false;
        int num = 0;
        // 判斷快指標是否走完即可
        while( fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if( slow == fast && slow != null){
                flag = true;
                break;
            }
        }
        // 2.得到環中節點數量
        if(flag == false){
            // 沒有環
            return null;
        }else{
            // 有環
            num += 1;
            slow = slow.next;
            while( slow != fast){
                num += 1;
                slow = slow.next;
            }
        }
        // 3.尋找環的入口:快指標先一步步地走環的節點數 n 步,慢指標再開始走
        // 因為有環的存在,所以快慢指標必在環的入口相遇(快指標先走的n步會在環中消耗掉
        fast = pHead;
        slow = pHead;
        for( int i = 0; i < num; i++){
            fast = fast.next;
        }
        while( slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

總結

這道題代表了一種解題思路,即將問題拆分成多個問題逐個解決。先判斷是否有環,是第一個問題;若有環則要找環的入口,是第二個問題;找環的入口可以通過環的性質實現,是第三個問題。

當然解題的思路也是看了答案才知道的,不過,這篇筆記的意義就在於以後能輕鬆寫出這種題目!