參考自:
(jark's blog)
鍊錶問題在面試過程中也是很重要也很基礎的一部分,鍊錶本身很靈活,很考查程式設計功底,所以是很值得考的地方。
節點定義如下:
struct node;
1、以o(1)時間刪除某個節點題目描述:給定鍊錶的頭指標和乙個節點指標,在o(1)時間刪除該節點。[google面試題]
//o(1)時間刪除鍊錶節點,從無頭單鏈表中刪除節點
void delnode(struct node* node)
需要注意的是該辦法並不適用於刪除尾節點。
參見 leetcode #237. delete node in a linked list
2、單鏈表的反轉
題目描述:輸入乙個單向鍊錶,輸出逆序反轉後的鍊錶。
分析:單鏈表的反轉通常有兩種,一種是使用三個臨時指標 pre、cur、next 在鍊錶上迴圈一遍即可,這是一種非遞迴的方法,另一種是使用遞迴的方法,要求思路清晰。
//非遞迴
class solution
cur -> next = pre;
return cur;
}};//遞迴
class solution
};
leetcode傳送門:
206. reverse linked list
3、刪除單鏈表倒數第k個節點
題目描述:輸入乙個單向鍊錶,輸出該鍊錶中倒數第k個節點。
分析:設定兩個指標 p1、p2,首先 p1 和 p2 都指向 head,然後 p2 向前走 k - 1步,然後 p1 和 p2 同時向前移動,直至 p2 走到鍊錶末尾,此時 p1 就指向倒數第 k 個節點。
class solution
int i = n - 1;
while(i) //用 i 來判斷給的 n 是否大於鍊錶長度,考慮到刪除倒數第n+1個(鍊錶長n),此處不能用 while(i--)
else
break;
}if(i)
return head;
while(p2 -> next != null)
p1 -> val = p1 -> next -> val;
p1 -> next = p1 -> next -> next;
return head;}};
參見 leetcode #19remove nth node from end of list
4、求鍊錶中間的節點
題目描述:求鍊錶的中間節點,如果鍊錶的長度為偶數,返回中間兩個節點的任意乙個,若為奇數,則返回中間節點。
分析:此題的解決思路和第3題「求鍊錶的倒數第 k 個節點」很相似。通過兩個指標來完成。用兩個指標從煉表頭節點開始,乙個指標每次向後移動兩步,乙個每次移動一步,直到快指標移到到尾節點,那麼慢指標即是所求。
**如下:
//求鍊錶的中間節點,對於長度為偶的鍊錶,返回中間節點中的第乙個
node* themiddlenode(node *head)
return slow;
}
5、判斷單鏈表是否存在環,並找到環入口節點題目描述:輸入乙個單向鍊錶,判斷鍊錶是否有環。如果鍊錶存在環,如何找到環的入口點?
分析:通過兩個指標,分別從鍊錶的頭節點出發,乙個每次向後移動一步,另乙個移動兩步,兩個指標移動速度不一樣,如果存在環,那麼兩個指標一定會在環裡相遇。按照 p2 每次兩步,p1 每次一步的方式走,發現 p2 和 p1 重合,確定了單向鍊錶有環路了。接下來,讓p2回到鍊錶的頭部,重新走,每次步長不是走2了,而是走1,那麼當 p1 和 p2 再次相遇的時候,就是環路的入口了。
原因目前還沒搞清楚。。先記住吧。
leetcode #141. linked list cycle
class solution
return false;}};
leetcode #
142. linked list cycle ii
class solution
}if(flag)
return p1;
}else
return null;}};
6、判斷兩個鍊錶是否相交題目描述:給出兩個單向鍊錶的頭指標(如下圖所示),
比如h1、h2,判斷這兩個鍊錶是否相交。這裡為了簡化問題,我們假設兩個鍊錶均不帶環。
解題思路:
1、直接迴圈判斷第乙個鍊錶的每個節點是否在第二個鍊錶中。但,這種方法的時間複雜度為o(length(h1) * length(h2))。顯然,我們得找到一種更為有效的方法,至少不能是o(n^2)的複雜度。
2、針對第乙個鍊錶直接構造hash表,然後查詢hash表,判斷第二個鍊錶的每個節點是否在hash表出現,如果所有的第二個鍊錶的節點都能在hash表中找到,即說明第二個鍊錶與第乙個鍊錶有相同的節點。時間複雜度為為線性:o(length(h1) + length(h2)),同時為了儲存第乙個鍊錶的所有節點,空間複雜度為o(length(h1))。是否還有更好的方法呢,既能夠以線性時間複雜度解決問題,又能減少儲存空間?
3、轉換為環的問題。把第二個鍊錶接在第乙個鍊錶後面,如果得到的鍊錶有環,則說明兩個鍊錶相交。如何判斷有環的問題上面已經討論過了,但這裡有更簡單的方法。因為如果有環,則第二個鍊錶的表頭一定也在環上,即第二個鍊錶會構成乙個迴圈鍊錶,我們只需要遍歷第二個鍊錶,看是否會回到起始點就可以判斷出來。這個方法時間複雜度為線性o(n),空間複雜度為o(1)。
4、進一步考慮「如果兩個沒有環的鍊錶相交於某一節點,那麼在這個節點之後的所有節點都是兩個鍊錶共有的」這個特點,我們可以知道,如果它們相交,則最後乙個節點一定是共有的。而我們很容易能得到鍊錶的最後乙個節點,所以這成了我們簡化解法的乙個主要突破口。那麼,我們只要判斷兩個鍊錶的尾指標是否相等。相等,則鍊錶相交;否則,鍊錶不相交。
所以,先遍歷第乙個鍊錶,記住最後乙個節點。然後遍歷第二個鍊錶,到最後乙個節點時和第乙個鍊錶的最後乙個節點做比較,如果相同,則相交,否則,不相交。這樣我們就得到了乙個時間複雜度,它為o((length(h1) + length(h2)),而且只用了乙個額外的指標來儲存最後乙個節點。這個方法時間複雜度為線性o(n),空間複雜度為o(1)。
7、擴充套件:鍊錶有環,如何判斷相交
題目描述:上面的問題都是針對鍊錶無環的,那麼如果鍊錶是有環的呢?上面的方法還同樣有效麼?
分析:如果有環且兩個鍊錶相交,則兩個鍊錶都有共同乙個環,即環上的任意乙個節點都存在於兩個鍊錶上。因此,就可以判斷一煉表上倆指標相遇的那個節點,在不在另一條鍊錶上。
8、擴充套件:兩鍊錶相交的第乙個公共節點
題目描述:如果兩個無環單鏈表相交,怎麼求出他們相交的第乙個節點呢?
分析:採用對齊的思想。計算兩個鍊錶的長度 l1 , l2,分別用兩個指標 p1 , p2 指向兩個鍊錶的頭,然後將較長鍊錶的 p1(假設為 p1)向後移動l2 - l1個節點,然後再同時向後移動p1 , p2,直到 p1 = p2。相遇的點就是相交的第乙個節點。
或者將鍊錶2的頭接在鍊錶1後,產生乙個環後將問題轉換成了求環的入口,參見上面已有的答案即可。
9、總結
可以發現,在鍊錶的問題中,通過兩個的指標來提高效率是很值得考慮的乙個解決方案,所以一定要記住這種解題思路。記住幾種
典型的鍊錶問題解決方案,很多類似的題目都可以轉換到熟悉的問題再解決。
資料結構與演算法 鍊錶
題目 合併兩個已經排序好的鍊錶 非遞迴和遞迴兩種 方法1 cpp view plain copy print color 000000 合併鍊錶.cpp 定義控制台應用程式的入口點。include stdafx.h include using namespace std struct listnod...
資料結構與演算法 鍊錶
在講述鍊錶之前讓我們對資料結構進行乙個簡單的回顧 我們知道,資料結構指的是描述實際問題中各個資料項節點之間的前後邏輯結構關係,即要麼是線性結構 即某一資料項的前繼節點和後繼節點有且只有乙個 要麼是非線性結構 即某一資料節點的前驅或者後繼節點不止乙個 在確定了實際資料項的資料結構之後,我們要採用某種儲...
資料結構與演算法 鍊錶
反轉鍊錶 def reverse head q none p heap while p temp p.next p.next q q pp temp return p判斷鍊錶環 def meetingnode head if not head return slow head fast head.n...