二叉樹在記憶體中的儲存方式主要分為兩大類:循序儲存結構與鏈式儲存結構。選擇哪種方式,取決於樹的形態與操作需求。
此法將滿二叉樹由上至下、由左至右編號,依序存入陣列。若節點在陣列中的索引為 i,則具備以下數學性質:
i = 0,該節點為根節點,無雙親。i > 0,其雙親索引為 (i - 1) / 2(整數除法)。2i + 1;右子節點索引:2i + 2。i > 0 且為奇數,則為左孩子,兄弟索引為 i + 1;若為偶數,則為右孩子,兄弟索引為 i - 1。k 的滿二叉樹需長度為 2^k - 1 的陣列。此結構極適合完全二叉樹或滿二叉樹,可 O(1) 計算親屬關係,空間效率最高。但對於一般二叉樹,需以特殊值(如 null 或 #)佔位空節點,若樹稀疏(如退化為鏈表),空間浪費極大(深度 k 需 2^k - 1 空間),此時應改用鏈式結構。
鏈式結構以節點物件加指標連結,彈性大、不浪費空間,是二叉樹最常用的儲存方式。
left、右指標 right。parent 指標,便於向上查找。
後續演算法均以二叉鏈表為基礎。
遍歷是指依特定順序訪問樹中每個節點一次且僅一次。本質是將非線性結構線性化,為各種樹操作奠基。
依遞歸定義:樹 = 根節點(D) + 左子樹(L) + 右子樹(R)。先左後右有三種經典順序:
| 遍歷方式 | 順序 | 口訣 |
|---|---|---|
| 先序 | D → L → R | 根左右 |
| 中序 | L → D → R | 左根右 |
| 後序 | L → R → D | 左右根 |
以圖例樹為例:
public class Node {
public object Data { get; private set; }
public Node Left { get; set; }
public Node Right { get; set; }
public Node(object data) { Data = data; }
public override string ToString() => Data.ToString();
}
public class BinaryTree {
private Node _head;
// 建構函式略(依字串 "ABCDE#F" 建樹,# 表空節點)
// 先序遍歷
public void PreOrder(Node node) {
if (node != null) {
Console.Write(node);
PreOrder(node.Left);
PreOrder(node.Right);
}
}
// 中序遍歷
public void MidOrder(Node node) {
if (node != null) {
MidOrder(node.Left);
Console.Write(node);
MidOrder(node.Right);
}
}
// 後序遍歷
public void AfterOrder(Node node) {
if (node != null) {
AfterOrder(node.Left);
AfterOrder(node.Right);
Console.Write(node);
}
}
}
從根節點開始,逐層由左至右訪問。需借助佇列實作,不使用遞歸。
D,訪問之。D 有左子節點,入佇列;同理處理右子節點。圖例樹層序結果:A B C D E F
public void LevelOrder() {
Queue queue = new Queue();
queue.Enqueue(_head);
while (queue.Count > 0) {
Node node = (Node)queue.Dequeue();
Console.Write(node);
if (node.Left != null) queue.Enqueue(node.Left);
if (node.Right != null) queue.Enqueue(node.Right);
}
}