Easy草稿★★★★★O(m + n) 時間 · O(1) 空間
Linked ListTwo PointersHash Table
Patterns↔️ 雙指針⛓️ 鏈結串列操作#️⃣ 雜湊
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

160Intersection of Two Linked ListsLinked ListEasyLinked List

給定兩個單向鏈結串列的頭節點 headAheadB,找出兩個鏈結串列的交叉節點。若無交叉則回傳 null。注意交叉是以節點引用判斷,不是節點值。

Example:

Input: listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 Output: Intersected at ‘8’

Intuition

TIP

核心思路:兩個指針分別從 A 和 B 出發,走完自己的串列後切換到對方的串列,最終會在交叉點相遇。

Approaches

1. HashSet — O(m + n) / O(m)
  • Idea: 先將 A 的所有節點存入 HashSet,再遍歷 B 找第一個存在於 Set 中的節點
  • Time: O(m + n)
  • Space: O(m) - 儲存 A 的所有節點
class Solution {
    fun getIntersectionNode(headA: ListNode?, headB: ListNode?): ListNode? {
        val visited = HashSet<ListNode>()
        var curr = headA
        while (curr != null) {
            visited.add(curr)
            curr = curr.next
        }
        curr = headB
        while (curr != null) {
            if (curr in visited) return curr
            curr = curr.next
        }
        return null
    }
}
⭐ 2. Two Pointers (Switch Lists) — O(m + n) / O(1)
  • Idea: 指針 A 走完 listA 後接到 listB 的頭,指針 B 走完 listB 後接到 listA 的頭,兩者會在交叉點相遇
  • Time: O(m + n)
  • Space: O(1)
class Solution {
    fun getIntersectionNode(headA: ListNode?, headB: ListNode?): ListNode? {
        var pA = headA
        var pB = headB
        while (pA !== pB) {
            pA = if (pA != null) pA.next else headB
            pB = if (pB != null) pB.next else headA
        }
        return pA
    }
}

WARNING

比較節點時必須用 !==(引用比較),不是 !=(值比較)。Kotlin 中 == 對 nullable 型別可能呼叫 equals。

🔑 Takeaways