某網際網路企業的筆試題:判斷單連結串列是否有環?如果有環,如何在O(N)的時間複雜度和O(1)的空間複雜度得到這個環的入口點?
問題1:如何判斷有環呢?
這個方法可能不只一種,但是在網上看到的那個類似於兩個人跑步的解法最容易讓人接受。兩個人跑步,同時跑,但一個人在跑的快,一個人跑的慢,如果他們在一個環形操場上跑的話,肯定他們要不只一次的相遇。判斷環也是這個原理,兩個指標p和q,一開始p和q都指向頭結點,如果p每次移動一步,q每次移動兩步,那麼p和q如果再一次相遇,那麼有環,如果走到null,那麼肯定沒環。
問題2:如何得到環的入口?
這個需要一點的推導,假設在p和q第二次相遇的時候(第一次相遇在頭結點)p走了s步,那麼q肯定走了2s步,所以有:
2*s-s=nr. 其中,r是環的長度,n是圈數。
如果記從頭結點到環入口點的距離為a,從換入口點到相遇點得距離為x,那麼有
s=a+x. (q和p相遇時,p肯定沒有走完一圈)
由上面兩個公式又能推出:
s=a+x=nr
如果連結串列總長度為L的話,那麼r=L-a, 有
a+x=(n-1)r+(L-a)
a=(n-1)r+(L-a-x)
其中L-a-x是p要走完一圈還要走的步數。
因此可以這樣找到環的起始點:p和q相遇後,再增加一個指標k,k指向連結串列的頭結點,然後k開始移動,一次一步,p繼續移動,一次一步,那麼當k走到環的入口點,p也一定到了環的入口點。因此k和p的相遇點,就是換的入口點。
擴充套件問題1:如何求迴圈連結串列上任一點a的最遠點b.
一個圓上最遠點就是同一條直徑上的另一個點嘛。
方法還是指標p和q, 均從a點出發,p一次走1步,q一次走2步,當q回到a時,p指的點就是另一端的點。因為q走一圈的時候,p肯定只走了半圈。
擴充套件問題2:判斷兩個單連結串列是否相交,如果相交,給出相交的第一個點(兩個連結串列都不存在環)
其實只要把第一連結串列首位相連,判斷相交和找相交點就變成了與問題1和2相同的問題。
其實判斷兩個單連結串列是否相交有更簡單的方法,只需要判斷連結串列的最後一個點是否相同就可以。因為如果相交,就像軌道並軌一樣,兩個連結串列的最後一個節點肯定是相同的。
還可以通過兩個棧來得到第一個相交的點,將Link A的節點全部壓入棧SA,將Link B的節點全部壓入棧SB,然後同時出棧,最後一個相同的點就是相交點。
還有另外一種判斷方法,容易得到Link A的長度LA,和Link B的長度LB,那麼假設LA>LB那麼LA先走LA-LB步,LB在從走,兩個指標肯定會相交,相交的第一個點就是要找的點。