學習路線/樹 觀念講解

🌳 樹狀結構(Tree)

樹是最常用的非線性資料結構之一。從檔案系統到 HTML DOM、從組織圖到決策樹,樹無所不在。 本頁從定義出發,一路帶你理解術語、二元樹類型、四種遍歷方式,並介紹比賽中最常用的三種建樹模式。

什麼是樹?

樹是一種很特別的圖。樹的定義是:任兩點之間都相通,而且沒有「環」的圖。 「環」是指繞圈子的循環路線。此外,樹的邊數永遠等於節點數 − 1。

123456
✅ 這是樹:任兩點相通、無環
1234紅色虛線形成環
❌ 這不是樹:存在環

樹狀結構術語

下面這張樹會作為術語解釋的範例。節點 1 是這棵樹的根(已強調)。

1234567
節點 (Node)
樹上的每一個點都是節點。圖中共有 7 個節點。
邊 / 枝 (Edge / Branch)
連接兩個節點的線。樹的邊數 = 節點數 − 1,圖中有 6 條邊。
根 (Root)
整棵樹的起點。本圖以節點 1 為根(任一節點都可以作為根)。
葉 / 外部節點 (Leaf)
沒有子節點的節點。圖中 4、5、6、7 都是葉。
父親 / 小孩 (Parent / Child)
相鄰兩點中靠近根者為父親,靠近葉者為小孩。節點 2 是 4、5 的父親。
祖先 / 後代 (Ancestor / Descendant)
父親的父親…皆為祖先;小孩的小孩…皆為後代。1 是 4 的祖先,4 是 1 的後代。
手足 (Sibling)
共享同一個父親的節點。4 與 5 是手足;2 與 3 也是。
分支度 (Degree)
一個節點的小孩數量。節點 1 的 degree = 2。
層級 (Level)
節點離根的距離。根通常是 level 0,其子節點是 level 1,以此類推。
高度 / 深度 (Height / Depth)
整棵樹上最大的層級。本圖的高度 = 2。

二元樹 (Binary Tree)

二元樹中,每個節點最多只有兩個子節點,分別稱為左子樹(left subtree)與右子樹(right subtree)。 依節點分佈可分為三種特殊形態:

歪斜二元樹

1234
每層只有左子樹(或都只有右子樹)

完整二元樹

123456
除最下層外其餘皆全滿,最下層靠左擺放

滿二元樹

1234567
每一層都全滿

樹的遍歷 (Tree Traversal)

遍歷是指「走訪樹上每一個節點」的過程。根據「根節點何時被處理」,可以分為四種方式:

  • 前序 (Preorder):Root → Left → Right
  • 中序 (Inorder):Left → Root → Right
  • 後序 (Postorder):Left → Right → Root
  • 層序 (Level Order):一層一層走完(BFS)

下方動畫可以切換四種遍歷方式,點選播放看節點如何被依序走訪:

Root → Left → Right

12345
已走訪順序
(尚未開始)

建樹模式 (Building Trees from Input)

💡 為什麼需要學建樹?

LeetCode 等平台會預先把 TreeNode 物件建好給你,學習者只需要專注在「寫函式」。 但在 APCS、ZeroJudge 等競賽中,你必須自己從輸入字串把樹建起來 — 而「建樹」這一步往往才是卡人的關鍵。以下三種模式分別對應不同的輸入型態與操作需求。

模式一:Dict 映射

場景:輸入已經列出每個節點的編號與左右子節點。

資料結構:defaultdict, key 是節點編號,value 是 (left, right) tuple。

輸入範例
3
1, 2, 3
2, -, -
3, -, -
建出的樹
123
Python 實作(計算樹高度)
from collections import defaultdict

n = int(input())
tree = defaultdict(lambda: (None, None))

for i in range(1, n + 1):
    parts = [p.strip() for p in input().split(',')]
    node = int(parts[0])
    if i == 1:
        root = node  # 樹的根節點
    left = None if parts[1] == '-' else int(parts[1])
    right = None if parts[2] == '-' else int(parts[2])
    tree[node] = (left, right)

def height(node):
    if node is None:
        return -1  # 空節點回傳 -1,葉節點高度為 0
    left_h = height(tree[node][0])
    right_h = height(tree[node][1])
    return max(left_h, right_h) + 1

print(height(root))

模式二:Class + BST Insert

場景:輸入是一串數值流,要依照 BST 規則(左子 < 父親 < 右子)建樹。

資料結構:自訂 class Node,每個節點持有 value / left / right。

輸入範例
1
5
5, 3, 8, 1, 4
建出的 BST
53814
Python 實作(建 BST 並輸出前序)
class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

n = int(input())
for _ in range(n):
    a = int(input())
    nodes = list(map(int, input().split(',')))
    root = Node(nodes[0])
    for v in nodes[1:]:
        current = root
        while True:
            if v < current.value:
                if current.left is None:
                    current.left = Node(v)
                    break
                current = current.left
            else:
                if current.right is None:
                    current.right = Node(v)
                    break
                current = current.right

    def preorder(node):
        if node:
            res.append(node.value)
            preorder(node.left)
            preorder(node.right)

    res = []
    preorder(root)
    print(' '.join(map(str, res)))

模式三:Parent 陣列 + Children 清單

場景:輸入是邊列表 (child, parent),且題目會問父親、手足、祖先等結構性問題。

資料結構:parents[i] 記每個節點的父親,children[p] 記每個節點的小孩清單。

輸入範例
5
2, 1
3, 1
4, 2
5, 2
2
4
3
建出的樹
12345
Python 實作(查詢 siblings)
from collections import defaultdict

n = int(input())
parents = [-1] * (n + 1)
children = defaultdict(list)
for i in range(n - 1):
    c, p = map(int, input().split(','))
    parents[c] = p
    children[p].append(c)

m = int(input())
for _ in range(m):
    node = int(input())
    p = parents[node]
    if p == -1:
        print(f'siblings {node} = {{}}')
    else:
        sibs = sorted([c for c in children[p] if c != node])
        print(f'siblings {node} = {{{",".join(map(str, sibs))}}}')

如何選擇建樹模式?

輸入格式常見操作推薦模式
每行給 node, left, right計算高度、遍歷模式一:Dict 映射
一串數值需要排序建樹BST 查找、遍歷模式二:Class + BST
邊列表 (child, parent)查父親 / 手足 / 祖先模式三:Parent + Children

下一步

觀念看完了,現在就來試試實戰題目吧!從經典的「比較兩棵樹是否完全相同」開始:

→ LeetCode 100 Same Tree(實戰練習)