1、如何判斷乙個單鏈表有環
2、如何判斷乙個環的入口點在**
3、如何知道環的長度
4、如何知道兩個單鏈表(無環)是否相交
5、如果兩個單鏈表(無環)相交,如何知道它們相交的第乙個節點是什麼
6、如何知道兩個單鏈表(有環)是否相交
7、如果兩個單鏈表(有環)相交,如何知道它們相交的第乙個節點是什麼
1、採用快慢步長法。令兩個指標p和q分別指向頭結點,p每次前進一步,q每次前進兩步,如果p和q能重合,則有環。可以這麼理解,這種做法相當於p靜止不動,q每次前進一步,所有肯定有追上p的時候。
我們注意到,指標p和q分別以速度為1和2
前進。如果以其它速度前進是否可以呢?
假設p和q分別以速度為v1和v2前進。如果有環,設指標p和q第一次進入環時,他們相對於環中第乙個節點的偏移位址分別為a和b
(可以把偏移位址理解為節點個數)
這樣,可以看出,鍊錶有環的充要條件就是某一次迴圈時,指標p和q的值相等,就是它們相對環中首節點的偏移量相等。
我們設環中的結點個數為n,程式迴圈了m次。
由此可以有下面等式成立:(mod(n)即對n取餘
)(a+m*v1)mod(n) = (b+m*v2) mod(n)
設等式左邊mod(n)的最大整數為k1,等式右邊mod(n)的最大整數為k2,則
(a+m*v1)-k1*n = (b+m*v2)-k2*n
整理以上等式:
m= |((k2-k1)*n+a-b)/( v2-v1)| ①
如果是等式①成立,就要使迴圈次數m為一整數。顯然如果v2-v1為1
,則等式成立。
這樣p和q分別以速度為v1和v2且|v2-v1|為1時,按以上演算法就可找出鍊錶中是否有環。當然|v2-v1|不為1時,也可能可以得出符合條件的m。
[cpp]
view plain
copy
bool
i***itsloop(slist *head)
return
!(fast == null || fast->next == null);
}
時間複雜度分析:假設甩尾(在環外)長度為 len1(結點個數),環內長度為 len2,鍊錶總長度為n,則n=len1+len2
。當p步長為1,q步長為2時,p指標到達環入口需要len1時間,p到達入口後,q處於**不確定,但是肯定在環內,此時p和q開始追趕,q最長需要len2時間就能追上p(p和q都指向環入口),最短需要1步就能追上p(p指向環入口,q指向環入口的前乙個節點)。事實上,每經過一步,q和p的距離就拉近一步,因此,經過q和p的距離步就可以追上p。因此總時間複雜度為o(n),n為鍊錶的總長度。
2、分別從煉表頭和碰撞點,同步地一步一步前進掃瞄,直到碰撞,此碰撞點即是環的入口。
證明如下:
鍊錶形狀類似數字 6 。
假設甩尾(在環外)長度為 a(結點個數),環內長度為 b 。
則總長度(也是總結點數)為 a+b 。
從頭開始,0 base 編號。
將第 i 步訪問的結點用 s(i) 表示。i = 0, 1 ...
當 i<a 時,s(i)=i ;
當 i≥a 時,s(i)=a+(i-a)%b 。
分析追趕過程。
兩個指標分別前進,假定經過 x 步後,碰撞。則有:s(x)=s(2x)
由環的週期性有:2x=tb+x 。得到 x=tb 。
另,碰撞時,必須在環內,不可能在甩尾段,有 x>=a 。
連線點為從起點走 a 步,即 s(a)。
s(a) = s(tb+a) = s(x+a)。
得到結論:從碰撞點 x 前進 a 步即為連線點。
根據假設易知 s(a-1) 在甩尾段,s(a) 在環上,而 s(x+a) 必然在環上。所以可以發生碰撞。
而,同為前進 a 步,同為連線點,所以必然發生碰撞。
綜上,從 x 點和從起點同步前進,第乙個碰撞點就是連線點。
[cpp]
view plain
copy
slist* findloopport(slist *head)
if(fast == null || fast->next == null)
return
null;
slow = head;
while
(slow != fast)
return
slow;
}
時間複雜度分析:假設甩尾(在環外)長度為 len1(結點個數),環內長度為 len2 。則時間複雜度為「環是否存在的時間複雜度」+o(len1)
3、從碰撞點開始,兩個指標p和q,q以一步步長前進,q以兩步步長前進,到下次碰撞所經過的操作次數即是環的長度。這很好理解,比如兩個運動員a和b從起點開始跑步,a的速度是b的兩倍,當a跑玩一圈的時候,b剛好跑完兩圈,a和b又同時在起點上。此時a跑的長度即相當於環的長度。
假設甩尾(在環外)長度為 len1(結點個數),環內長度為 len2 ,則時間複雜度為「環是否存在的時間複雜度」+o(len2)。
4、法一:
將鍊錶a的尾節點的next指標指向鍊錶b的頭結點,從而構造了乙個新鍊錶。問題轉化為求這個新鍊錶是否有環的問題。
時間複雜度為環是否存在的時間複雜度,即o(length(a)+length(b)),使用了兩個額外指標
法二:兩個鍊錶相交,則從相交的節點起,其後的所有的節點都是都是兩個鍊錶共有的。因此,如果它們相交,則最後乙個節點一定是共有的。因此,判斷兩鍊錶相交的方法是:遍歷第乙個鍊錶,記住最後乙個節點。然後遍歷第二個鍊錶,到最後乙個節點時和第乙個鍊錶的最後乙個節點做比較,如果相同,則相交。
時間複雜度:o(length(a)+length(b)),但是只用了乙個額外指標儲存最後乙個節點
5、將鍊錶a的尾節點的next指標指向鍊錶b的頭結點,從而構造了乙個環。問題轉化為求這個環的入口問題。
時間複雜度:求環入口的時間複雜度
6、分別判斷兩個鍊錶a、b是否有環(注,兩個有環鏈表相交是指這個環屬於兩個鍊錶共有)
如果僅有乙個有環,則a、b不可能相交
如果兩個都有環,則求出a的環入口,判斷其是否在b鍊錶上,如果在,則說明a、b相交。
時間複雜度:「環入口問題的時間複雜度」+o(length(b))
7、分別計算出兩個鍊錶a、b的長度la和lb(環的長度和環到入口點長度之和就是鍊錶長度),參照問題3。
如果la>lb,則鍊錶a指標先走la-lb,鍊錶b指標再開始走,則兩個指標相遇的位置就是相交的第乙個節點。
如果lb>la,則鍊錶b指標先走lb-la,鍊錶a指標再開始走,則兩個指標相遇的位置就是相交的第乙個節點。
時間複雜度:o(max(la,lb))
鍊錶的常見面試題
鍊錶的基本操作 逆序列印單鏈表 刪除鍊錶的非尾結點,要求 不能遍歷鍊錶 在鍊錶pos位置前插入值到data的結點 查詢鍊錶的中間結點,要求只能遍歷一次鍊錶 查詢鍊錶的倒數第k個結點,要求只能遍歷一次鍊錶 刪除鍊錶的倒數第k個結點,要求只能遍歷一次鍊錶 用單鏈表實現約瑟夫環 鍊錶的逆置 三個指標 鍊錶...
常見面試題
1.get和post的區別 1 本質區別 get是向伺服器請求資料,post是向伺服器傳送資料。2 伺服器獲取值的方式 get方式提交的資料,伺服器端使用request.querystring獲取變數的值。post方式提交的資料,伺服器端使用request.form獲取資料。3 安全性 get安全效...
常見面試題
1 一行 實現1 100之和 lst i for i in range 1,101 print sum lst 2 如何在乙個函式內部修改全域性變數?在函式內部新增 global 變數名 這樣就可以在本地作用域定義全域性作用域了 name 大明 def eat name1 global name n...