題目
劍指 Offer 52. 兩個連結串列的第一個公共節點
思路1(棧)
- 若兩個連結串列相遇,則從它開始相遇的地方到連結串列末尾應該都是相同的,那麼我們可以將兩個連結串列分別放入兩個棧中,然後依次迴圈比較兩個棧頂的節點,同時用
pre
記錄上一個節點,直到當前兩個棧頂節點不相等,那麼pre
節點就是他們開始相遇的地方
程式碼
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
LinkedList<ListNode> stack1 = new LinkedList<>();
LinkedList<ListNode> stack2 = new LinkedList<>();
// 使用兩個棧分別儲存兩個連結串列的元素
while (headA != null) {
stack1.push(headA);
headA = headA.next;
}
while (headB != null) {
stack2.push(headB);
headB = headB.next;
}
// 若兩個連結串列存在相同的節點,則兩個棧pop出來的節點應該是一樣的
// 如果pop出來的不一樣,說明上一個pop出來相同節點才是相遇開始的節點
// 因此我們需要pre來記錄上一個相同的節點,初始值要為null,如果沒有相遇,那麼會直接返回null
ListNode pre = null;
while (!stack1.isEmpty() && !stack2.isEmpty()) {
ListNode p1 = stack1.pop();
ListNode p2 = stack2.pop();
if (p1 != p2) {
break;
}
// 如果沒有相遇,就不會執行這條語句,因此此時pre為 null,返回的值也是 null
pre = p1;
}
// 返回結果
return pre;
}
}
複雜度分析
- 時間複雜度:\(O(N+M)\)
- 空間複雜度:\(O(N+M)\)
思路2(差值法)
- 先分別計算兩個連結串列的長度記為
lengthA
和lengthB
,得到他們的差值n
,讓長的那個連結串列先走n
步,然後兩個連結串列再一起走,第一個相等的地方,則是連結串列開始相遇的地方
程式碼
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lengthA = 0;
int lengthB = 0;
ListNode pA = headA;
ListNode pB = headB;
// 先獲取兩個連結串列的長度
while (pA != null) {
pA = pA.next;
lengthA++;
}
while (pB != null) {
pB = pB.next;
lengthB++;
}
// 計算連結串列長度差n,將長的連結串列先走n步
int n = lengthA > lengthB ? lengthA - lengthB : lengthB - lengthA;
while (lengthA > lengthB && n > 0) {
headA = headA.next;
n--;
}
while (lengthB > lengthA && n > 0) {
headB = headB.next;
n--;
}
// 然後兩個連結串列才開始同時一起走
// 知道兩個節點相等 或者 都為null的時候結束迴圈
while (headA != headB) {
headA = headA.next;
headB = headB.next;
}
return headA;
}
}
複雜度分析
- 時間複雜度:\(O(N+M)\)
- 空間複雜度:\(O(1)\)
思路3(雙指標)
- 使用兩個指標
pA
和pB
分別指向兩個連結串列,然後同時前進,如果到了連結串列末尾,就跳到另一條連結串列的頭節點(要保證不會再跳回來,否則導致死迴圈)
- 在這過程中,要判斷節點是否相等,第一次相等的地方就是相交的地方,如果沒有那就返回
null
- 在
while
中的判斷條件要為當前兩個節點是否相等,因為不這樣的話,如果不存在相交的節點,那麼迴圈就會一直重複下去;如果判斷條件為當前兩個節點是否相等的話,如果沒有相交節點也不會一直迴圈下去,因為兩個指標遍歷的長度都為兩個連結串列長度之和,所以最後都會指向null
,此時都為null
相等,跳出迴圈
程式碼
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pA = headA;
ListNode pB = headB;
// 判斷是否相等,遇到相同節點,結束迴圈;如果連結串列中沒有相同節點,那麼跳出迴圈的條件是他們都為 null 的時候
while (pA != pB) {
// 判斷的要為當前節點
// 如果判斷的是next節點,那麼到時候就會導致pA、pB永遠不會是null
if (pA == null) {
pA = headB;
} else {
pA = pA.next;
}
if (pB == null) {
pB = headA;
} else {
pB = pB.next;
}
}
return pA;
}
}
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(1)\)
思路4(雜湊表)
- 先選擇其中一個連結串列,將其節點全部加入到雜湊表中,然後再開始遍歷另一個連結串列,同時也加入到雜湊表中
- 如果加入的節點和雜湊表衝突了,那麼這個節點就是交點
程式碼
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> set = new HashSet<>();
// 遍歷一次,將連結串列A裡的所有節點新增到set中
while (headA != null) {
set.add(headA);
headA = headA.next;
}
// 再遍歷一次連結串列B,同時將連結串列的節點新增到set中,如果新增失敗返回false,那麼說明找到相同的那個節點了,直接返回該節點
while (headB != null) {
if (!set.add(headB)) {
return headB;
}
headB = headB.next;
}
// 如果遍歷連結串列B的時候都沒有找到,說明不存在相同的節點,就返回 null
return null;
}
}
複雜度分析
- 時間複雜度:\(O(N+M)\)
- 空間複雜度:\(O(N)\),雜湊表所佔空間大小