劍指Offer-38-兩個連結串列的第一個公共節點
題目
輸入兩個連結串列,找出它們的第一個公共結點。
解析
預備知識
2個單向連結串列相交後的示意圖如下所示:
從上圖的得知,若兩個連結串列相交,那麼這兩個連結串列應該具有相同的尾部,也就是說呈現出Y型。因為單向連結串列中只有一個next域指向後繼結點,所以從第一個相交點開始都是兩個連結串列的公共部分,而不是我們思維慣性以為相交後就岔開了,不明白的再仔細看看上圖即可。
思路一
空間換時間的做法,既然若相交,必有公共節點。所以我們可以申請一個set,遍歷第一個連結串列,存放所有的節點。然後遍歷第二個節點,若發現set中已存在該節點,那麼就說明從這個節點開始兩個連結串列相交,該節點就是第一個公共節點。
/**
* 空間換時間
* @param pHead1
* @param pHead2
* @return
*/
public static ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1 == null || pHead2 == null) {
return null;
}
Set<ListNode> set = new HashSet<>();
while(pHead1 != null) {
set.add(pHead1);
pHead1 = pHead1.next;
}
while(pHead2 != null) {
if(set.contains(pHead2)) {
return pHead2;
}
pHead2 = pHead2.next;
}
return null;
}
思路二
嘗試從不申請額外空間的做法。我們發現若兩個連結串列相交,最後一個節點必然相同。所以我們可以遍歷兩個連結串列使其都走向最後一個節點。此時,若發現最後這兩個節點不相同,則說明連結串列不相交。若相同,則說明連結串列相交。但是最後一個節點可能不是第一個相交點,我們需要向前回溯尋找第一個相同相交點。對於這種先考察後進來的節點,也就是後進先出的情況,使用棧再好不過了,只需在第一步遍歷的時候把連結串列各自的節點放到各自的棧中,最後不斷比較棧頂即可。若棧頂相同,儲存該棧頂,然後彈出,繼續比較,直到棧為空或者棧頂不相同,那麼上一次的棧頂就是第一個公共節點。但是這個做法還是藉助了額外的空間,故程式碼實現就不貼出了。 但是你也可以嘗試遞迴,哈哈,我就不寫了,挺簡單的。
我們要解決就是如何找到第一個相交點,由於連結串列的特性,只能從前往後遍歷。所以問題轉化為如何從開頭遍歷尋找第一個相交點。之前的分析已知知道了,若兩個連結串列相交,必然有公共的尾部。我們的目標是如何使兩個連結串列可以同時走到公共尾部的第一個節點。因為公共尾部的第一個節點到開頭各自不同,所以我們求出2個連結串列的長度的差值,使長的連結串列先走這個差值長度,然後再同時走兩個連結串列,這時就可以不斷比較當前遍歷節點的地址是否相同即可。
/**
* 相交的連結串列必有相同的尾部
* @param pHead1
* @param pHead2
* @return
*/
public static ListNode FindFirstCommonNode2(ListNode pHead1, ListNode pHead2) {
if(pHead1 == null || pHead2 == null) {
return null;
}
ListNode p = pHead1, q = pHead2;
int length1 = 1, length2 = 1;
while(p.next != null) {
p = p.next;
length1++;
}
while(q.next != null) {
q = q.next;
length2++;
}
if(p == q) {
int diff = Math.abs(length1 - length2);
if(length1 > length2) {
p = pHead1;
q = pHead2;
} else {
p = pHead2;
q = pHead1;
}
while(diff-- > 0) {
p = p.next;
}
while(p != q) {
p = p.next;
q = q.next;
}
return p;
}
return null;
}
思路三
牛逼的做法,牛逼的思路,異常的清爽程式碼。
該思路的做法也是如何解決思路中使兩個連結串列遍歷到離尾部相同長度的位置。思路二使用是事先走一遍統計長度,然後使長的連結串列先走差值步長,這樣就可以保證2個連結串列現在離尾部相同距離相同。
該思路則是採用了2個連結串列互補形成2者長度相同的做法。比如我兩個棍子長度為n,m,我可以把m拼在n後面,n拼在m後面。這樣我就有2個相同長度的棍子,n+m。我們實際的做法,並不是真正的拼接,而是跳轉。
1. p,q指向各自的連結串列
2. 判斷p與q是否相同,不相同,判斷p是否走到連結串列1的末尾,若是,則p接著從連結串列2頭部開始走,若沒有,則繼續遍歷下一個節點
3. 判斷q是否走到連結串列2的末尾,若是,則q接著從連結串列1頭部開始走,若沒有,則繼續遍歷下一個節點
以上出現結果可能有以下幾種:
1. 當連結串列1與連結串列2長度一致時,那麼在各自連結串列上遍歷即可完成判斷是否有相交點
2. 因為當長度一致時,兩個連結串列齊頭並進,若有公共尾部,必然能走到相交點,若沒有,最後都會等於null而結束迴圈
3. 當連結串列1與連結串列2長度不一致時,短的連結串列的會率先結束自己的遍歷,並開始在長的連結串列中遍歷。當長的連結串列完成自己的遍歷時,並開始在短的連結串列遍歷時。這時在長的連結串列遍歷已經走完了兩者差值長度。因為是同時走的,這時他們走的距離是相同的,因此他們對於到末尾的距離也是相同,因為2者總長都為n + m。剩下就是繼續判斷是否相交了,因為保證了到末尾的距離一樣,所以必然能同時走到第一個相交點。
/**
* 利用補齊法達到使兩個連結串列長度相同
* @param pHead1
* @param pHead2
* @return
*/
public static ListNode FindFirstCommonNode3(ListNode pHead1, ListNode pHead2) {
ListNode p = pHead1, q = pHead2;
while(p != q) {
p = p == null ? pHead2 : p.next;
q = q == null ? pHead1 : q.next;
}
return p;
}
總結
可以結合畫圖來發掘思路,但是請注意以上的解法的都是假設連結串列不存在環。若存在環,則可能稍微複雜一點,具體可以參考如何判斷兩個連結串列是否相交併求出相交點和如何判斷單連結串列是否有環、環的入口、環的長度和總長
相關文章
- 力扣 - 劍指 Offer 52. 兩個連結串列的第一個公共節點力扣
- 劍指offer——兩個連結串列的第一個公共結點C++C++
- 兩個連結串列的第一個公共節點
- 【LeetCode】【連結串列】劍指 Offer 52. 兩個連結串列的第一個公共節點 思路解析和程式碼LeetCode
- 兩個連結串列的第一個公共結點
- JZ-036-兩個連結串列的第一個公共結點
- 【劍指offer】【3】輸入一個連結串列,從尾到頭列印連結串列每個節點的值。
- 劍指offer——連結串列中倒數第k個結點
- 力扣 - 劍指 Offer 22. 連結串列中倒數第k個節點力扣
- 2024/11/27 【連結串列】LeetCode 24 兩兩交換連結串列中的節點 & LeetCode 19 刪除連結串列的倒數第N個節點LeetCode
- 牛客網高頻演算法題系列-BM10-兩個連結串列的第一個公共結點演算法
- 劍指 Offer 25. 合併兩個排序的連結串列 JavaScript實現排序JavaScript
- 劍指offer-----刪除連結串列中的重複節點
- K個節點翻轉連結串列
- 【LeetCode連結串列#9】圖解:兩兩交換連結串列節點LeetCode圖解
- **24. 兩兩交換連結串列中的節點****19.刪除連結串列的倒數第N個節點****面試題 02.07. 連結串列相交****142.環形連結串列II**面試題
- [連結串列]leetcode1019-連結串列中的下一個更大節點LeetCode
- Day4(連結串列)|24. 兩兩交換連結串列中的節點 & 19.刪除連結串列的倒數第N個節點 & 面試題 02.07. 連結串列相交 &142.環形連結串列II面試題
- Day 4 | 24. 兩兩交換連結串列中的節點 、 19.刪除連結串列的倒數第N個節點 、面試題 02.07. 連結串列相交 、142.環形連結串列II面試題
- 第四天:● 24. 兩兩交換連結串列中的節點 ● 19.刪除連結串列的倒數第N個節點 ● 面試題 02.07. 連結串列相交 ● 142.環形連結串列II面試題
- leetcode 24 兩兩交換連結串列中的節點LeetCode
- 定義一個函式,輸入一個連結串列的頭節點,反轉該連結串列並輸出反轉後連結串列的頭節點函式
- 【連結串列問題】打卡2:刪除單連結串列的第 K個節點
- 1019. 連結串列中的下一個更大節點
- 程式碼隨想錄day4 | 24 兩兩交換連結串列節點 19 刪除倒數第n個節點 142 環形連結串列
- leetcode 24.兩兩交換連結串列中的節點LeetCode
- LeetCode 24. 兩兩交換連結串列中的節點LeetCode
- 24. 兩兩交換連結串列中的節點 (中等)
- 兩個有序連結串列序列的交集
- 166. 連結串列倒數第n個節點
- 程式碼隨想錄第4天 | 24. 兩兩交換連結串列中的節點、19.刪除連結串列的倒數第N個節點、面試題 02.07. 連結串列相交、142.環形連結串列II面試題
- 合併兩個有序連結串列
- 【連結串列問題】打卡8:複製含有隨機指標節點的連結串列隨機指標
- 連結串列中的節點每k個一組翻轉
- 0011 刪除連結串列的倒數第N個節點
- 程式碼隨想錄演算法訓練營第四天 | 連結串列 24.兩兩交換連結串列中的節點 19.刪除連結串列的倒數第N個節點 142.環形連結串列II演算法
- 劍指offer:刪去連結串列中重複的節點。(題解原始碼加圖解)原始碼圖解
- 程式碼隨想錄演算法訓練營day04|24.兩兩交換連結串列中的節點,19.刪除連結串列的倒數第N個節點,面試題 02.07.連結串列相交,142.環形連結串列II演算法面試題