【Leetcode】python - [145] Binary Tree Postorder Traversal 個人解法筆記 (內含範例程式碼)

題目出處

145. Binary Tree Postorder Traversal

難度

easy

題目分類

Stack, Tree, Depth-First Search, Binary Tree

解 Tree 樹問題的重點整理

這一篇的前身是以下連結,我們繼續在這篇整理重點

【Leetcode】python – [144] Binary Tree Preorder Traversal 個人解法筆記 (內含範例程式碼) 內含 處理 Tree 樹問題的重點

Tree 基本中的基本,我們一定至少需要學會兩種 parse tree 的方式,

  • 一種是 iterative (直接的取)
  • 另一種是 recursive (遞迴的取)

基本上這兩種都務必熟悉,未來解題時萬一一種解不出來,就可以嘗試用另外一種方法。

個人範例程式碼 - recursive 版

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        self.dfs(root, ans)
        return ans

    def dfs(self, root, ans):
        if root:
            self.dfs(root.left, ans)
            self.dfs(root.right, ans)
            ans.append(root.val)

算法說明

recursive 的 parse tree 基本上可以結合 dfs 的概念,
而我們需要另外寫一個 function dfs(),因為我們會需要把答案帶著走,原題目的函數沒辦法直接遞迴。

老實說個人覺得,Tree 系列的題目用 recursive 的概念是最接近原本學習知識觀念的解法XD。
寫起來非常直覺,而且稍微變化一下其他的解就都能直接跑出來。

主要變化的範圍如下,然後依照 (M)RL、R(M)L、RL(M) 改一下順序,全部的解法就都出來了!

  • self.dfs(root.left, ans)
  • self.dfs(root.right, ans)
  • ans.append(root.val)

corner case 特殊情況處理

當樹為空 [] 或 為只有 root [1] 時,需要注意。

Boundary conditions/ Edge conditions 邊際情況處理

處理沒有節點的情況, if not root 直接回傳。

另外一種漂亮的解法:直接只處理 「if root:」,我們才做事情。

個人範例程式碼 - iterative 版

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        stack = [root]     
        # LR(M) -> (M)RL
        while(stack):
            if root: # left to leaf
                ans.append(root.val) # add M first
                stack.append(root.left) # record later
                root = root.right
            else: # can not left
                root = stack.pop(-1)       

        return ans[::-1]

算法說明

我們要處理的問題變得比較麻煩一些,變成 LR(M),
但因為我們其實都是在控制 (M) 出現的時機,
因此比較好的方式是我們把它提造最前面去做,

因此我們以 (M)RL 去做一棵樹,而最後「再反序」。
我們分兩個 part

可以往下走的情況, (此處是往右先)

我們可以往右就右到底。

先加 (M),存左,往右走

if root: # left to leaf
   ans.append(root.val) # add M first
   stack.append(root.left) # record later
   root = root.right   

不能往下走,就往左走的情況

表示上述 「if root」 找不到結果的情況,
pop 出「最後一個」「分歧點」,
也就是「回到上一個岔路往左走」。

else: # can not left
   root = stack.pop(-1)        

corner case 特殊情況處理

當樹為空 [] 或 為只有 root [1] 時,需要注意。

Boundary conditions/ Edge conditions 邊際情況處理

結束條件為 Stack 為空,我們運用 Stack 可以幫我們保存的概念。

Reference

  • 此系列解法個人的其他文章
  • 個人認為整理的最完整的解法
  • 很用心的 DFS 製圖
  • 結合 visited 的很酷的 solution
  • - [Python solutions (recursively and iteratively).](https://leetcode.com/problems/binary-tree-preorder-traversal/discuss/45290/Python-solutions-(recursively-and-iteratively).) - [Python recursive and iterative solutions.](https://leetcode.com/problems/binary-tree-postorder-traversal/discuss/45786/Python-recursive-and-iterative-solutions.)