樹 — LeetCode 實戰範例

樹與堆積優先度 3/5 — 值得會 — 多半是必備模式的變形值得會 更新於 Nov 12, 2026

範圍 — binary_tree.md 各模板的二元樹題目集:每題歸在它化約到的模板底下,只寫它多出來的那一步,模板本身不在這裡重講。 另見:binary_tree.md — 這些題目化約到的模板;tree.md — 樹的觀念、走訪策略與 Morris threading;tree_lca_distance.md — LCA、距離與路徑題;bst_examples.md — 用到 BST 順序的題目;tree_codec.md 與 tree_construction.md — 樹的序列化與建構。

LeetCode 題目清單

概覽

經典的二元樹題目,依各題化約到的 binary_tree.md 模板分組,順序與該檔的模板順序一致。模板在 binary_tree.md,這一份只放題目,每張卡只寫這題比模板多了什麼。凡是某個模板的錨點題,或已在別份檔案完整解過的題目,Index 只放一列指過去,不在這裡再抄一份。

Index

高度 vs 深度,以及深度搜尋用 -1 當「沒找到」的哨兵值,統一在 binary_tree.md § 0-5 講一次。

遞迴走訪範例

Boundary of Binary Tree — LC 545

關鍵 — 邊界是三段走訪接起來的:左邊界用前序(往下走時記錄),所有葉子由左到右,再接右邊界用後序(回程時記錄,所以是由下往上)。兩段邊界走訪都不記葉子,角落的節點才不會被列兩次。

歸約到:binary_tree § Template 1 — 一次走訪變成三次,每段都依它在邊界上被讀出的順序記錄。

python
# python
# LC 545 - Boundary of Binary Tree
# IDEA: root, then the left boundary top-down, the leaves left to right, the right boundary bottom-up;
#       neither boundary walk records a leaf, so no node appears twice
# time = O(n), space = O(h)
def boundaryOfBinaryTree(root):
    if not root:
        return []
    result = [root.val]

    def is_leaf(node):
        return not node.left and not node.right

    def left_boundary(node):               # pre-order: record on the way down
        while node and not is_leaf(node):
            result.append(node.val)
            node = node.left if node.left else node.right

    def leaves(node):                      # every leaf, left to right
        if not node:
            return
        if is_leaf(node):
            result.append(node.val)
            return
        leaves(node.left)
        leaves(node.right)

    def right_boundary(node):              # post-order: record on the way back up
        stack = []
        while node and not is_leaf(node):
            stack.append(node.val)
            node = node.right if node.right else node.left
        while stack:
            result.append(stack.pop())

    if not is_leaf(root):                  # a lone root is not also a leaf
        left_boundary(root.left)
        leaves(root)
        right_boundary(root.right)
    return result

陷阱

  • 邊界上的節點只有右子節點:[1,2,null,null,3,…] — 沒有左子節點時,左邊界要接著往右子節點走。只跟著 .left 走會提早停下。
  • root = [1]:root 本身是葉子,再收一次葉子會印出 [1, 1]。要先擋掉 root。
  • root = [1,null,2,3,4]:root 沒有左子樹,所以左邊界只有 [1];葉子是 3, 4,右邊界是 2,答案 [1,3,4,2]。

層序範例

Minimum Depth of Binary Tree — LC 111

關鍵 — BFS 依深度順序抵達節點,所以第一個出佇列的葉子就是最淺的,搜尋可以就此停止;遞迴版的 min 則必須特別處理只有一個子節點的節點,因為空的那一側不是葉子。

歸約到:binary_tree § Template 2 — 層序佇列,加上遇到第一個葉子就提早結束。

python
# python
# LC 111 - Minimum Depth of Binary Tree
# IDEA: BFS carrying the depth; the first leaf dequeued is the shallowest
# time = O(n), space = O(w)   w = maximum width of a level; stops at the first leaf
from collections import deque

def minDepth(root):
    if not root:
        return 0
    queue = deque([(root, 1)])
    while queue:
        node, depth = queue.popleft()
        if not node.left and not node.right:   # the first leaf BFS reaches is the shallowest
            return depth
        if node.left:
            queue.append((node.left, depth + 1))
        if node.right:
            queue.append((node.right, depth + 1))

陷阱

  • 遞迴寫成 1 + min(minDepth(left), minDepth(right)),在 [2,null,3,null,4,null,5,null,6] 上會回傳 1:空的左子節點得 0,但它不是葉子。正確答案是 5。缺一個子節點時,只往另一邊遞迴。
  • LC 104(最大深度)沒有這個陷阱:max 永遠不會挑空的那一側。

Binary Tree Right Side View — LC 199

關鍵 — 從右邊看到的節點,就是每一層由左到右的最後一個節點;先記下該層的大小,才知道哪一次出佇列是最後一個。

歸約到:binary_tree § Template 2 — 每層的歸納只保留最後一個節點。

python
# python
# LC 199 - Binary Tree Right Side View
# IDEA: level-order BFS; record the last node of each level
# time = O(n), space = O(w)
from collections import deque

def rightSideView(root):
    result = []
    if not root:
        return result
    queue = deque([root])
    while queue:
        level_size = len(queue)
        for i in range(level_size):
            node = queue.popleft()
            if i == level_size - 1:            # the last node of the level is the one you see
                result.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
    return result

陷阱

  • 只沿著右子節點走:[1,2,3,4] 第 2 層看得到 4,雖然它掛在左子樹下。答案是 [1,3,4],不是 [1,3]。
  • 先走右再走左、在每個新深度記下第一個節點的 DFS 也行,空間 O(h);len(result) == depth 就是「第一次到這個深度」的判斷。

由走訪序建樹範例

Construct Binary Tree from Inorder and Postorder Traversal — LC 106

關鍵 — postorder 的最後一個值是 root,所以從尾端往回讀;倒著讀是 root、右、左,因此必須先建右子樹再建左子樹。

歸約到:binary_tree § Template 3 — 同樣用 inorder 切分,只是 root 從另一端取,兩個遞迴呼叫對調。

python
# python
# LC 106 - Construct Binary Tree from Inorder and Postorder Traversal
# IDEA: postorder[-1] is the root; its inorder index splits [lo, hi]; build RIGHT first
# time = O(n), space = O(n)   the index map, plus O(h) recursion
def buildTree(inorder, postorder):
    index_of = {val: i for i, val in enumerate(inorder)}
    post_idx = [len(postorder) - 1]           # next root, read from the END of postorder

    def build_range(lo, hi):                  # the subtree whose inorder slice is [lo, hi]
        if lo > hi:
            return None
        root = TreeNode(postorder[post_idx[0]])
        post_idx[0] -= 1
        mid = index_of[root.val]
        root.right = build_range(mid + 1, hi)  # RIGHT first: postorder read backwards is root, right, left
        root.left = build_range(lo, mid - 1)
        return root

    return build_range(0, len(inorder) - 1)

陷阱

  • 先建左子樹:從 postorder 尾端讀到的下一個值屬於右子樹,所以 inorder = [9,3,15,20,7]、postorder = [9,15,7,20,3] 建出來 20 會跑到左邊。
  • 每次呼叫都切片(inorder[:idx]、postorder[:idx])並呼叫 inorder.index(...),各是 O(n),在歪斜的樹上就是 O(n²);用索引 map 加一個移動的指標才是 O(n)。

根到節點狀態範例

Count Good Nodes in Binary Tree — LC 1448

關鍵 — 一個節點是不是「好節點」只取決於它上方的最大值,所以這個最大值當參數往下傳;它是 int,每次呼叫各有一份,回程時沒有東西要復原。

歸約到:tree_lca_distance § Template 4 — 根到節點的狀態是一個數字而不是 list,所以回溯那一步就不見了。

python
# python
# LC 1448 - Count Good Nodes in Binary Tree
# IDEA: pre-order DFS carrying the maximum on the path from the root
# time = O(n), space = O(h)
def goodNodes(root):
    def dfs(node, max_so_far):
        if not node:
            return 0
        good = 1 if node.val >= max_so_far else 0
        max_so_far = max(max_so_far, node.val)   # an int: each call has its own copy
        return good + dfs(node.left, max_so_far) + dfs(node.right, max_so_far)

    return dfs(root, root.val)

陷阱

  • 用 > 取代 >= 會漏掉等於目前最大值的節點:[3,3,null,4,2] 中第二個 3 是好節點,答案是 3,用 > 會回傳 2。
  • 起始值設成 0 而不是 root.val,遇到負數就錯;root 一定是好節點,所以用它自己的值(或 -inf)當起點。

Maximum Difference Between Node and Ancestor — LC 1026

關鍵 — 一條路徑上最大的 |祖先 − 子孫| 就是這條路徑的最大值減最小值,所以把這兩個數往下帶, 路徑結束時讀出 high − low 即可。不用比較任何一對,也用不到 BST 的順序。

歸約到:tree_lca_distance § Template 4 —— 沿根到葉路徑往下帶狀態; 和 bst § Template 4 的上下界是同樣兩個值,只是拿來擴張而不是檢查。

python
# python
# LC 1026 - Maximum Difference Between Node and Ancestor
# IDEA: carry the path's min and max down; at the end of a path the answer is max - min
# time = O(n), space = O(h)
def maxAncestorDiff(root):
    def dfs(node, low, high):
        if not node:
            return high - low                    # the path ended: its widest spread
        low = min(low, node.val)
        high = max(high, node.val)
        return max(dfs(node.left, low, high), dfs(node.right, low, high))

    return dfs(root, root.val, root.val)

陷阱

  • 每個節點只跟父節點比:LC 1026 的範例 1, [8,3,10,1,6,null,14,null,null,4,7,13],會回傳 5(8 − 3)而不是 7(8 − 1,相隔兩條邊)。
驗證(LC 98) 祖先最大差(LC 1026)
往下帶的 合法區間 路徑上的最小值與最大值
節點落在區間外時 回傳 false 擴張區間
子節點結果用什麼合併 and max

高度往上傳範例

Maximum Depth of Binary Tree — LC 104

關鍵 — 一棵樹的深度是兩棵子樹中較深者再加一;這就是模板回傳的高度,而且 root 自己的回傳值就是答案,不需要全域變數。

歸約到:binary_tree § Template 5 — 只用回傳值,不用全域變數。

python
# python
# LC 104 - Maximum Depth of Binary Tree
# IDEA: post-order; my depth = 1 + the deeper child's depth
# time = O(n), space = O(h)
def maxDepth(root):
    if not root:
        return 0
    return 1 + max(maxDepth(root.left), maxDepth(root.right))

陷阱

  • 數邊而不是數節點:LC 104 數的是節點,所以 maxDepth(None) = 0,單一節點是 1。None 回傳 -1(數邊的慣例)會讓 [3,9,20,null,null,15,7] 得到 2。
  • 10⁴ 個節點的鏈,遞迴就有 10⁴ 層;Python 需要 sys.setrecursionlimit,或改用逐層計數的層序 BFS。

Balanced Binary Tree — LC 110

關鍵 — 高度和判定結果在同一次後序走訪裡一起回傳:-1 代表「下面某處已經不平衡」,每個祖先直接往上傳,所以每個節點只走一次。

歸約到:binary_tree § Template 5 — 高度同時兼任失敗訊號,而不是拿去更新全域變數。

python
# python
# LC 110 - Balanced Binary Tree
# IDEA: post-order height; -1 is the "unbalanced" sentinel that short-circuits upward
# time = O(n), space = O(h)
def isBalanced(root):
    def height(node):                      # -1 means "already unbalanced below here"
        if not node:
            return 0
        left = height(node.left)
        if left == -1:
            return -1
        right = height(node.right)
        if right == -1:
            return -1
        if abs(left - right) > 1:
            return -1
        return 1 + max(left, right)

    return height(root) != -1

第二種解法 — 由上而下,以及為什麼比較貴。 多數人第一次寫的版本是先在 root 比較兩邊高度,再遞迴進兩個子節點,所以 height() 會對每個祖先把整棵子樹重走一次。它是對的;放在這裡只是為了面試官接下來會問的複雜度問題。

python
# python
# LC 110 - Balanced Binary Tree (top-down)
# IDEA: at every node compare the two subtree heights, then recurse into both children
# time = O(n log n) on a balanced tree, O(n^2) upper bound; space = O(h)
def isBalanced(root):
    def height(node):
        if not node:
            return 0
        return 1 + max(height(node.left), height(node.right))

    if not root:
        return True
    if abs(height(root.left) - height(root.right)) > 1:
        return False
    return isBalanced(root.left) and isBalanced(root.right)

用「遞迴每一層的工作量 × 層數」來算。每種形狀各自給出一個每層成本和一個層數,絕不能兩種形狀各取一個:

平衡的樹 歪斜的樹(一條鏈)
層數 log n n
第 d 層的工作量 2^d 次呼叫 × 子樹大小 n/2^d ≈ n 1 次呼叫 × 子樹大小 n − d
總和 n · log n = O(n log n) — T(n) = 2T(n/2) + O(n) n + (n−1) + … + 1 = O(n²) — T(n) = T(n−1) + O(n)

換成以節點來數:height() 會因為每個祖先各走訪一個節點一次,所以總和就是所有深度的和,即 n ×(平均深度)。只有平衡的形狀能把那個深度壓在 log n。

陷阱

  • O(n² log n) 是把歪斜情況的級數(本身已經是所有層的總和)乘上平衡情況的高度得到的。它把層數算了兩次。
  • 在鏈上,這段由上而下的程式其實在 root 就結束了(高度 n−1 和 0 差超過 1),所以 O(n²) 是這種遞迴模式的成本,由沒有提早結束的程式全額支付,例如在每個節點都呼叫 height 的天真直徑寫法。
  • 由下而上版本只在兩次呼叫之後才檢查一次 -1:仍是 O(n),但會白白走完一個已經失敗節點的右子樹。在遞迴右邊之前先測 left == -1。

樹形 DP 範例

Binary Tree Cameras — LC 968

關鍵 — 每次呼叫回傳一個狀態 — 未覆蓋、有攝影機、已覆蓋 — 由父節點做決定。空子節點算已覆蓋,所以葉子會回報「未覆蓋」,逼父節點裝攝影機,一台可以蓋三層而不是一層。未覆蓋的節點自己什麼都不放:交給父節點決定。

歸約到:binary_tree § Template 9 — 三個狀態取代 (take, skip),每個節點用貪心規則取代取 max。

python
# python
# LC 968 - Binary Tree Cameras
# IDEA: post-order DFS returning one of 3 states; the parent makes the decision
# time = O(n), space = O(h)
def minCameraCover(root):
    UNCOVERED, CAMERA, COVERED = 0, 1, 2
    cameras = 0

    def dfs(node):
        nonlocal cameras
        if not node:
            return COVERED                 # so a leaf reports UNCOVERED and its parent acts
        left = dfs(node.left)
        right = dfs(node.right)
        if left == UNCOVERED or right == UNCOVERED:   # test this FIRST
            cameras += 1
            return CAMERA
        if left == CAMERA or right == CAMERA:
            return COVERED
        return UNCOVERED                   # decide nothing: the parent places the camera

    if dfs(root) == UNCOVERED:             # the root has no parent to defer to
        cameras += 1
    return cameras
java
// java
// LC 968 - Binary Tree Cameras
// IDEA: post-order, 3 states; an uncovered child forces a camera here
// time = O(n), space = O(h)
private static final int UNCOVERED = 0, CAMERA = 1, COVERED = 2;
private int cameras = 0;

public int minCameraCover(TreeNode root) {
    cameras = 0;
    if (dfs(root) == UNCOVERED) {
        cameras++;                              // the root has no parent to defer to
    }
    return cameras;
}

private int dfs(TreeNode node) {
    if (node == null) {
        return COVERED;
    }
    int left = dfs(node.left);
    int right = dfs(node.right);
    if (left == UNCOVERED || right == UNCOVERED) {
        cameras++;
        return CAMERA;
    }
    if (left == CAMERA || right == CAMERA) {
        return COVERED;
    }
    return UNCOVERED;
}

試跑 — root = [1,2,3,4,5,null,null,6,7]

text
        1
       / \
      2   3
     / \
    4   5
   / \
  6   7

post-order: 6, 7, 4, 5, 2, 3, 1
  6, 7 : both children null (COVERED)  -> UNCOVERED
  4    : left UNCOVERED                -> camera #1, CAMERA
  5    : leaf                          -> UNCOVERED
  2    : left CAMERA, right UNCOVERED  -> the uncovered test wins: camera #2, CAMERA
  3    : leaf                          -> UNCOVERED
  1    : right UNCOVERED               -> camera #3, CAMERA
answer 3

陷阱

  • 先測「有子節點裝了攝影機」再測「有子節點未覆蓋」:上例的節點 2 會回傳 COVERED,讓 5 附近完全沒有攝影機。
  • 忘了 root:[0] 是一個沒有父節點、未覆蓋的葉子,答案是 1,不是 0。
  • 空節點回傳 UNCOVERED 會讓每個葉子都裝攝影機;空節點必須是「已覆蓋但沒有攝影機」的狀態。

結構修改範例

Invert Binary Tree — LC 226

關鍵 — 翻轉一棵樹就是在每個節點交換兩個子節點指標;遞迴回傳翻好的子樹、父節點再接回去,所以兩邊的結果都要在任一指標被覆寫之前算好。

歸約到:binary_tree § Template 10 — 回傳重建好的子樹再重新指派;沒有刪除任何東西。

python
# python
# LC 226 - Invert Binary Tree
# IDEA: post-order; invert both subtrees, then swap them
# time = O(n), space = O(h)
def invertTree(root):
    if not root:
        return None
    left = invertTree(root.left)           # compute BOTH before assigning either
    right = invertTree(root.right)
    root.left = right
    root.right = left
    return root
java
// java
// LC 226 - Invert Binary Tree
// IDEA: post-order; cache both inverted subtrees, then swap
// time = O(n), space = O(h)
public TreeNode invertTree(TreeNode root) {
    if (root == null) {
        return null;
    }
    TreeNode left = invertTree(root.left);     // cache both results first
    TreeNode right = invertTree(root.right);
    root.left = right;
    root.right = left;
    return root;
}

陷阱

  • root.left = invertTree(root.right); root.right = invertTree(root.left); — 第二行讀到的是第一行剛覆寫的左指標,所以 [2,1,3] 會變成 [2,3,3] 這種亂掉的形狀。Python 的 root.left, root.right = f(root.right), f(root.left) 之所以安全,只是因為右邊會先整個算完。

Flatten Binary Tree to Linked List — LC 114

關鍵 — 每次呼叫把自己的子樹攤平,並回傳子樹的尾巴(前序中的最後一個節點);父節點就能用 O(1) 接成 node → 左鏈 → 右鏈:把原本的右子樹掛在左鏈的尾巴上,不必一路走下去找。

歸約到:binary_tree § Template 10 — 後序重新接線,回傳的是尾巴而不是新的 root。

python
# python
# LC 114 - Flatten Binary Tree to Linked List
# IDEA: post-order; flatten both sides, splice node -> left chain -> right chain, return the tail
# time = O(n), space = O(h)
def flatten(root):
    def flatten_tail(node):                # flatten node's subtree, return its LAST node
        if not node:
            return None
        left_tail = flatten_tail(node.left)
        right_tail = flatten_tail(node.right)
        if left_tail:                      # splice: node -> left chain -> right chain
            left_tail.right = node.right
            node.right = node.left
            node.left = None
        if right_tail:                     # the tail is the last non-empty box
            return right_tail
        if left_tail:
            return left_tail
        return node

    flatten_tail(root)

試跑 — root = [1,2,5,3,4,null,6]

text
flatten_tail(3) -> 3              flatten_tail(4) -> 4
flatten_tail(2): left_tail 3, right_tail 4
                 3.right = 4, 2.right = 3, 2.left = None   => 2 -> 3 -> 4,  return 4
flatten_tail(6) -> 6
flatten_tail(5): no left; right_tail 6                     => 5 -> 6,       return 6
flatten_tail(1): left_tail 4, right_tail 6
                 4.right = 5, 1.right = 2, 1.left = None   => 1 -> 2 -> 3 -> 4 -> 5 -> 6

陷阱

  • 在 left_tail.right = node.right 之前寫 node.right = node.left,原本的右子樹就丟了:先讀出來,或先暫存。
  • 找尾巴的順序是右 → 左 → 自己,和走訪順序相反:有右半段時,鏈就結束在右半段。有右子節點但沒有左子節點的節點,仍要回傳 right_tail。
  • 不回傳尾巴、改在每個節點用 while cur.right 走下去找,是 O(n·h),在歪斜的樹上是 O(n²)。

不適用時 — 追問要求 O(1) 額外空間:那是 tree.md § Template 1 的 Morris 重接,不用遞迴,把每棵左子樹最右邊的節點接到右子節點。

兩棵樹同步走範例

Symmetric Tree — LC 101

關鍵 — 一棵樹對稱,就是它的左右子樹互為鏡像,也就是把 LC 100 用在兩棵子樹上、配對交叉:外側配外側(a.left、b.right),內側配內側(a.right、b.left)。

歸約到:binary_tree § Template 12 — 一棵樹和自己比較,子節點交叉配對。它只在一個地方解,遞迴與迭代兩種寫法、試跑和直接配對的 bug 都在:dfs.md § LC 101。

陷阱

  • 逐層比較數值、檢查每層是不是回文,會漏掉形狀:[1,2,2,null,3,null,3] 去掉 null 後每層數值都是回文,但它不對稱。
  • a.left 配 b.left 檢查的是相等,不是對稱。

Merge Two Binary Trees — LC 617

關鍵 — 兩棵樹都有節點的地方,合併後的節點是兩者之和;只有一棵有的地方,整棵子樹原封不動拿來用,遞迴就停在那裡,不必複製。

歸約到:binary_tree § Template 12 — 同步走訪回傳新節點,而不是布林值。

python
# python
# LC 617 - Merge Two Binary Trees
# IDEA: walk both trees in lockstep; sum where both exist, reuse the lone subtree otherwise
# time = O(min(n1, n2)), space = O(min(h1, h2))
def mergeTrees(root1, root2):
    if not root1:
        return root2                       # reuse the other subtree as it is
    if not root2:
        return root1
    merged = TreeNode(root1.val + root2.val)
    merged.left = mergeTrees(root1.left, root2.left)
    merged.right = mergeTrees(root1.right, root2.right)
    return merged

陷阱

  • 遞迴進單獨的那棵子樹去複製它,成本是 O(n1 + n2),而不是只算重疊部分;原樣回傳才是重點。
  • 把和寫進 root1 再回傳(很多解法這樣做)也會被接受,但它改動了輸入;要說清楚你用的是哪一種。

Flip Equivalent Binary Trees — LC 951

關鍵 — 每個節點的子節點都可能被交換過,所以兩種配對都接受:同步檢查 left-left / right-right,或 left-right / right-left。

歸約到:binary_tree § Template 12 — 每個節點試兩種配對,用 or 接起來。

python
# python
# LC 951 - Flip Equivalent Binary Trees
# IDEA: Same Tree, but the children pair may also be matched crosswise
# time = O(n), space = O(h)   values are unique, so at most one pairing can match at each node
def flipEquiv(root1, root2):
    if not root1 or not root2:
        return root1 is root2
    if root1.val != root2.val:
        return False
    same = flipEquiv(root1.left, root2.left) and flipEquiv(root1.right, root2.right)
    flipped = flipEquiv(root1.left, root2.right) and flipEquiv(root1.right, root2.left)
    return same or flipped

陷阱

  • O(n) 的界依賴數值唯一:有重複值時,兩種配對都可能遞迴很深,最差情況比線性還差。
  • 先把兩棵樹正規化(每個節點的子節點依值排序)在這題也行,但只因為值是唯一的;兩路檢查才是通用寫法。

Reverse Odd Levels of Binary Tree — LC 2415

關鍵 — 在完美二元樹裡,一層中第 i 個節點和它鏡像位置的節點,可以用同步走兩半的方式找到:外側配外側、內側配內側,和 LC 101 的配對完全一樣;反轉一層就是交換每一對的值。

歸約到:binary_tree § Template 12 — LC 101 的鏡像配對,在奇數層交換值而不是比較值。

python
# python
# LC 2415 - Reverse Odd Levels of Binary Tree
# IDEA: walk mirror pairs in lockstep; swap their values on odd levels
# time = O(n), space = O(log n)   the tree is perfect, so its height is log n
def reverseOddLevels(root):
    def pair(a, b, level):                 # a and b are mirror positions on the same level
        if not a:                          # perfect tree: a and b are empty together
            return
        if level % 2 == 1:
            a.val, b.val = b.val, a.val
        pair(a.left, b.right, level + 1)   # outer pair
        pair(a.right, b.left, level + 1)   # inner pair

    pair(root.left, root.right, 1)
    return root

陷阱

  • 交換節點而不是交換值,就得在兩邊重接父節點;這題只需要交換 val。
  • a.left 配 b.left 會把位置 i 對到 i + half,不是它的鏡像:一層是 1 … 8 時會變成 5 6 7 8 1 2 3 4,而不是 8 7 6 5 4 3 2 1。

不適用時 — 樹不是完美二元樹,或你偏好直觀的做法:用層序 BFS 收集每個奇數層再反轉它的值(binary_tree § Template 2),空間 O(w)。