(二度挑戰)優化演算法練習 Leetcode 94 Binary Tree Inorder Traversal - Easy
最近開始進 Tree 題,原本以為只是換個資料結構,結果一看到題目給的樹圖跟 array 表示法,腦袋就先打結一半。
這篇先記錄一下 Binary Tree Inorder Traversal 的理解過程,因為它看起來只是「走訪」,但對我來說其實是搞懂 Tree 題的入口。
拆解題目
Binary Tree Inorder Traversal
Given the
rootof a binary tree, return the inorder traversal of its nodes’ values.
題目要我回傳一棵 binary tree 的 inorder traversal 結果。
但我一開始看到 inorder 根本沒有直覺,只知道它好像是 Tree traversal 的其中一種,腦中只剩下:
- preorder
- inorder
- postorder
但到底誰先誰後,真的會瞬間失憶。
自我挑戰 — Tree 題的第一個卡點不是程式,是看懂樹
這題最先卡住我的,不是 recursion,也不是 traversal,而是:
1 | [3,9,20,null,null,15,7] |
這個到底在表達什麼。
一開始我會被圖的位置騙到,尤其是像下面這種感覺:
1 | 3 |
我腦中會冒出很多奇怪問題:
- 為什麼
null null明明寫在20後面,卻算在9那邊? 7看起來沒有正正站在20下面,為什麼還算是20的 child?- 這到底是圖形位置重要,還是 array 順序重要?
後來才慢慢釐清:
LeetCode 這種 Tree 的 array 表示法,其實是 Level Order,也就是 BFS 一層一層排下來。 重點不是圖看起來怎麼擺,而是 parent-child 對應關係。
也就是說:
1 | [3,9,20,null,null,15,7] |
其實表示的是:
1 | 3 |
9 的左右 child 都不存在,只是 LeetCode 沒把 null 畫出來而已。
這一段真的卡很久,但一旦想通,就會知道:
1 | Tree 題不能太相信圖的物理位置,要相信結構。 |
Inorder 到底是什麼順序?
| Traversal | 規律 | 記法 |
|---|---|---|
| Preorder | 根 → 左 → 右 | Root Left Right |
| Inorder | 左 → 根 → 右 | Left Root Right |
| Postorder | 左 → 右 → 根 | Left Right Root |
工程師很好記的方式
把 root 想像成一個 console.log 的位置:
1 | function dfs(node) { |
1 |
|
Inorder traversal 就是:
1 | 1 → 2 → 3 |
也就是:
- 先去左邊
- 再處理自己
- 最後去右邊
走訪順序 != 記錄時機
這一點在剛開始學 Tree 很容易搞混,所以我們開始聚焦「記錄時機」。
如果題目要回傳陣列,那就還是要:
1 | result.push(node.val); |
所以 traversal 題其實很像是:
1 | 走訪節點 + 記錄順序 |
而不是單純走過去而已。
recursion 其實還沒很難,難的是知道什麼時候 push
這題的 recursion 架構其實很簡單:
1 | var inorderTraversal = function (root) { |
但這題真正的重點不是語法,而是:
1 | 什麼時候記錄 node.val |
如果是 inorder,就要放在:
1 | 左邊跑完之後,右邊之前 |
也就是這一行:
1 | result.push(node.val); |
的位置。
解題思路小記
我這題的腦內流程其實很亂:
- array 表示法看不懂
- 圖的位置在誤導我
- inorder 到底是哪個順序
console.log算不算記錄- 原來 traversal 題的重點是 push 的時機
後來才慢慢整理出來:
1 | LeetCode 的 tree array 是 BFS level order |
解題後感想
這題看起來很基礎,但其實對我來說它是:
1 | 真正開始理解 Tree 題的第一步 |
以前會覺得 Tree traversal 就是背三種順序,但這次重新寫之後,才比較真的理解:
- Tree 結構怎麼看
null代表什麼- inorder 到底是在幹嘛
- recursion 裡哪一行是在「記錄」
有時候簡單題真正難的不是解法,而是把那些原本模糊的概念真的釐清。
希望之後再看到 Tree 題時,不會先被圖嚇到。