Description

給定一棵二元樹的根節點,回傳其後序遍歷 (Postorder Traversal) 的節點值列表。後序遍歷順序為:左子樹 -> 右子樹 -> 根。

Example:

Input: root = [1,null,2,3] Output: [3,2,1]

Intuition#

後序遍歷:先遞迴走左、右子樹,最後才處理當前節點。

  • 後序遍歷順序為 Left -> Right -> Root
  • 迭代版本可以利用「反轉的前序遍歷」技巧:前序是 Root -> Left -> Right,改為 Root -> Right -> Left 後反轉即得 Left -> Right -> Root

Approaches#

1: Recursive DFS#

  • 概念: 遞迴先走左子樹,再走右子樹,最後處理當前節點
  • 時間複雜度: O(n)
  • 空間複雜度: O(h)
class Solution {
    fun postorderTraversal(root: TreeNode?): List<Int> {
        val result = mutableListOf<Int>()
        fun dfs(node: TreeNode?) {
            if (node == null) return
            dfs(node.left)
            dfs(node.right)
            result.add(node.`val`)
        }
        dfs(root)
        return result
    }
}

⭐ 2: Iterative (反轉前序)#

  • 概念: 修改前序遍歷為 Root -> Right -> Left,最後將結果反轉即為後序遍歷
  • 時間複雜度: O(n)
  • 空間複雜度: O(n),需要存放完整結果後反轉
class Solution {
    fun postorderTraversal(root: TreeNode?): List<Int> {
        val result = mutableListOf<Int>()
        if (root == null) return result
        val stack = ArrayDeque<TreeNode>()
        stack.addLast(root)
        while (stack.isNotEmpty()) {
            val node = stack.removeLast()
            result.add(node.`val`)
            node.left?.let { stack.addLast(it) }
            node.right?.let { stack.addLast(it) }
        }
        return result.reversed()
    }
}

🔑 Takeaways#

  • Pattern: 樹的遍歷 — 後序遍歷常用於需要先處理子樹再處理父節點的場景(如刪除樹、計算子樹大小)
  • 關鍵技巧: 迭代後序遍歷可以透過「修改前序 + 反轉」的技巧簡化實作,避免複雜的雙棧或標記法