超難鏈表題

現有兩個單鏈表,一個長度爲M,一條長度爲N,可能有環也可能無環,若他們相交,則給出他們相交的第一個節點,否則返回null,要求空間複雜度O(1)、空間複雜度O(M+N)。

這題信息量很大,需要拆分:

1.如何判斷單鏈表有無環:

有環鏈表的判定方式:只要他能在遍歷的過程中遍歷到一個節點兩次,那就是有環的。

所以我們可以準備一個hash,遍歷鏈表:

A.遇到了hash中未加入的節點,加入hash

B.遇到了hash中加入過的節點,有環

C.鏈表遍歷完了,指針指向了null,此時必定無環

 

O(1)複雜度下如何得到單鏈表有環無環呢?需要利用神器-快慢指針。

我們讓快慢指針在這個鏈表上跑,如果有環,他們一定會相遇:

證明:設不屬於環的部分長度爲m,屬於環部分的長度爲n,如果他們相遇,一定是在環上【因爲非環的地方由於快指針跑得快,他會一直在慢指針前面】,設他們跑了x次。

於是我們有式子:(1*x-m)%n==(2*x-m)%n,由於快指針跑得快,因此我們可以將上式變形爲(x-m)-k1*n==(2*x-m)-k2*n(k2>k1),移項後我們得到x=(k2-k1)*n,這是一定有解的。

我們可以證明得k2-k1=1:

首先,由於快指針是一次跳兩步的,所以當快指針走完了整條鏈【以所有節點被訪問一次爲基準】,此時不管這個單鏈表是完全是一個環還是一條直線加一個環,慢指針所在位置肯定裏入環點距離不超過環長一半,而這時快指針在入環節點或入環節點的前一個節點,此時只要證在這次的遍歷環過程中兩個指針會相遇即可。

1)由於現在慢指針離環終點還有大於一半環長的距離,因此,在慢指針跳到終點前快指針必定會跨過慢指針一次

2)分情況討論:

A.直線部分L和環部分長度C均爲奇數或偶數,那麼快指針經過(L+C)/2跳轉到入環節點,此時慢指針在(L+C)/2位置,此時快慢指針相距(L+C)/2-L=(C-L)/2,而因爲L和C的奇偶性相同,所以C-L必定爲偶數,於是經過(C-L)/2步兩者相遇,而快指針跳一次環需要的時間爲C/2,C/2>(C-L)/2,所以可知快指針在第二次遍歷環的時候就會撞上慢指針,最終慢指針沒有走完一環而快指針沒有走完第二環,於是k2-k1=0,並且可知k2=1,k1=0,x=1*n。

B.直線部分L、環部分長度C爲一奇一偶,那麼快指針經過(L+C+1)/2跳轉到入環節點的前一個節點,此時慢指針在(L+C+1)/2位置,此時快慢指針相距(L+C+1)/2-L-1=(C-L-1)/2,由於C、L奇偶性不同,所以(C-L-1)爲偶數,可整除2。於是經過(C-L-1)/2步兩個指針相遇,然而因爲(C-1)/2>(C-L-1)/2,所以可知快指針在第二次遍歷環的時候就會撞上慢指針,最終慢指針沒有走完一環而快指針沒有走完第二環,於是k2-k1=0,並且可知k2=1,k1=0,x=1*n。

另外,當他們相遇後,如果將快指針指向頭節點並變爲慢指針,兩個慢指針繼續遍歷,相等的時候對應的節點就是入環的節點。

證明:因爲k1=0,所以第一次相遇時,慢指針的位置爲(x-m)-0*n==x-m(x=1*n)->n-m,慢指針距離走完環還有的距離是n-(n-m)=m,得證。

快慢指針判環:

1)快慢指針同時在環上跑,如果快指針遍歷到了NULL,則必定無環。

2)若快慢指針相遇,將快指針指向頭節點,並將速度改爲慢指針,新慢指針和舊慢指針同時跑,第一個相遇點即使入環點loop。

 

單鏈表的情況分類:

                                                                      1.無環

                                                                 2.有環

                    3.只有環

也就是,單鏈表有環的情況下,必定是先無環【無環的長度可爲0】後有環,否則,會出下圖情況:

這樣乍一看是先有環後無環的,但是會發現,ERROR節點居然有兩個next指針,這不符合單鏈表的基本特徵。

 

現在我們解決了判環的問題,現在來求第一個相交的節點:

A.兩條無環單鏈表相交:

不可能的情況如下圖:還是之前說的,ERROR有兩個next域,不符合單鏈表的條件。

弱化約束:如果不要求空間複雜度,可以先遍歷一遍鏈表1,將所有節點打入hash,再遍歷鏈表2,每次遍歷節點,去hash中查詢,如果已經有記錄,那麼這個節點必定是第一個相交節點,直接返回true。如果鏈表2都遍歷完了,說明沒有相交節點,返回false。

強化約束:由上面的圖分析,我們可以知道,如果兩條單鏈表相交,從他們相交的節點開始,一直到尾節點,都是一樣的。因此,我們可以借用樹上找公共祖先的思想,分別遍歷兩條鏈表得到他們的長度和尾節點,len1,tail1,len2,tail2。

a.tail1!=tail2,兩條鏈表不可能相交

b.tail1==tail2,兩條鏈表必定相交,但是第一個相交節點未必是尾節點,設dif=abs(len1-len2):

1)如果len1>len2,先將len1走dif,此時兩條鏈表的開始指針到尾節點長度相等,同時開始遍歷,將第一次相等處的節點返回即可

2)如果len2>len1,先將len2走dif,。。。。。。同上

 

B.一條鏈表有環,一條鏈表無環,不可能相交:因爲當無環單鏈表和有環單鏈表相交時,由於有環單鏈表前面部分無環後面部分有環,因此從相交處開始到尾節點一定有一部分屬於環,而只要有一部分屬於環,就不可能滿足無環條件。或者這麼看,一旦相交,尾節點相同,而尾節點是屬於環的,此時要求無環單鏈表有環,無法滿足。示意圖如下,A不可能無環:

C.兩條有環鏈表

分三種情況:

1.兩個各自不相交:

從A鏈表的入環節點開始,遍歷下去,知道轉回自己,如果期間都沒有碰到B鏈表的入環節點,就是兩個有環但各自不相交。

 

2.在相同的入環節點前相交

因爲有相同的入環節點,所以第一個相交節點肯定是在入環節點或者更前,將環切掉,問題退化成兩條單鏈表相交問題。

 

3.在環上相交,注意next域方向,並沒有出現一個節點兩個next的情況,因此是滿足的

從A鏈表的入環節點開始,遍歷下去,知道轉回自己,如果期間碰到B鏈表的入環節點,就是兩個有環鏈表相交,此時任意返回兩個入環節點的一個即可,因爲都在一個環上,環可以當成是一個無限長的直線,所以兩個入環節點都可以作爲第一個相交節點。

 

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