題目描述:給定一棵二元樹的根節點 root,請回傳其**中序遍歷(Inorder Traversal)**的節點值列表。中序遍歷的順序為:左子樹 → 根節點 → 右子樹(Left → Root → Right)。
解題思路:最直覺的方式是遞迴,但題目進階要求考慮**迭代(Iterative)解法。迭代法使用一個顯式堆疊(Explicit Stack)**模擬遞迴的呼叫堆疊:維護一個當前指針 cur 和一個 stack。每次循環先將 cur 及其所有左子節點全部壓入堆疊(一路往左走到底);當無法再往左時,從堆疊彈出一個節點,記錄其值(這就是「訪問根節點」的時機),然後將 cur 移動到該節點的右子節點,繼續重複左子樹壓棧的流程。只要 cur != nullptr 或堆疊非空,迴圈就持續進行。這個模式準確地模擬了遞迴中序遍歷的行為。
時間複雜度:O(n) — 每個節點恰好被壓入堆疊一次、彈出一次,並被加入結果一次,n 為樹的節點總數。
空間複雜度:O(h) — 堆疊的最大深度等於樹的高度 h。最壞情況(完全左斜樹)為 O(n),平衡二元樹為 O(log n)。不計輸出結果陣列的話,空間複雜度即為 O(h)。
1. Initialize result = [], stack = [], cur = root
2. While cur != null OR stack is not empty:
a. Inner loop: while cur != null:
- Push cur onto stack
- cur = cur.left (go left as far as possible)
b. cur = stack.pop() (backtrack to the most-recent unvisited node)
c. Append cur.val to result (visit the node)
d. cur = cur.right (move to right subtree)
3. Return result方法一:遞迴(Recursive)
最簡潔的實作:定義輔助函式 void dfs(TreeNode* node, vector<int>& res),先遞迴處理左子樹,再將 node->val 加入結果,再遞迴處理右子樹。程式碼只需 5 行,邏輯清晰。缺點是使用系統呼叫堆疊,最壞情況深度 O(n),有堆疊溢位風險;且無法輕易暫停/恢復遍歷(不適合做迭代器)。時間 O(n)、空間 O(h)。
方法二:Morris 遍歷(Morris Traversal,O(1) 空間)
利用樹中葉節點的空指針作為臨時「線索(Thread)」,在遍歷時動態建立和恢復這些線索,完全不需要額外堆疊或遞迴呼叫堆疊。對於每個節點,若左子樹存在,找到左子樹的最右節點(中序前驅),將其 right 指向當前節點(建立線索);遍歷結束後再恢復原始結構。時間複雜度 O(n)(每條邊最多被訪問兩次),空間複雜度真正達到 O(1),但實作複雜度較高。