JZ23 連結串列中環的入口結點
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; } }
總結
這道題代表了一種解題思路,即將問題拆分成多個問題逐個解決。先判斷是否有環,是第一個問題;若有環則要找環的入口,是第二個問題;找環的入口可以通過環的性質實現,是第三個問題。
當然解題的思路也是看了答案才知道的,不過,這篇筆記的意義就在於以後能輕鬆寫出這種題目!