題目描述:給定一棵二元樹(非 BST)及兩個節點 p 和 q,找出它們的最近公共祖先(LCA)。LCA 定義為:在樹中同時是 p 和 q 的祖先且深度最大的節點(節點可以是自身的祖先)。
解題思路:由於這是一般二元樹(非 BST),無法利用大小關係導航,必須完整搜尋。使用後序 DFS 遞迴:對每個節點,分別在左右子樹中搜尋 p 和 q。若左子樹回傳非空(找到 p 或 q)且右子樹也回傳非空,代表 p 和 q 分別在當前節點的兩側,當前節點就是 LCA,回傳當前節點。若只有一側回傳非空,代表兩個節點都在同一側(或其中一個就是當前節點的子孫),回傳那一側的非空結果。這個遞迴設計保證找到的第一個「兩側皆非空」的節點必然是最近公共祖先。
時間複雜度:O(n) — 最壞情況需遍歷所有節點(當目標節點在樹的最深處時)。
空間複雜度:O(h) — 遞迴呼叫堆疊深度等於樹高 h;最壞情況(退化鏈)為 O(n),平衡樹為 O(log n)。
1. Define lca(node, p, q): a. If node is null OR node == p OR node == q: return node b. left = lca(node.left, p, q) c. right = lca(node.right, p, q) d. If left != null AND right != null: return node // LCA found e. Return left if left != null, else right 2. Return lca(root, p, q)
父節點雜湊表 + 集合 O(n):第一次 DFS 記錄每個節點的父節點到雜湊表;接著從 p 出發向上收集所有祖先到集合;再從 q 出發向上,第一個出現在集合中的節點即為 LCA。需要 O(n) 額外空間,但邏輯非常直觀,易於理解。
歐拉路徑 + 區間最小值 (RMQ) O(n) 預處理 / O(1) 查詢:將樹轉換為歐拉路徑(Euler Tour),LCA 問題轉化為區間最小深度查詢(RMQ)。使用稀疏表(Sparse Table)可達到 O(1) 查詢,適合需要大量 LCA 查詢的場景(離線批次處理)。實作複雜度高,不適合單次查詢。