二叉搜尋樹 (Binary Search Tree, BST) 是基礎資料結構,支援 O(log n) 平均插入、查找、刪除。本文提供生產級 Java 實現:節點類、BST 核心操作、前/中/後序遍歷 (遞歸與迭代雙版本)、完整測試代碼,並解析關鍵邏輯。
public class TreeNode {
public Integer data;
public TreeNode left, right, parent; // 父指針便於刪除操作
public TreeNode(Integer data) { this.data = data; }
@Override public String toString() { return "TreeNode[" + data + "]"; }
}
public class SearchTree {
public TreeNode root;
public long size;
/** 插入:不允許重複鍵 */
public Boolean addTreeNode(Integer data) {
if (root == null) { root = new TreeNode(data); size++; return true; }
TreeNode cur = root, parent = null;
while (cur != null) {
parent = cur;
if (data < cur.data) cur = cur.left;
else if (data > cur.data) cur = cur.right;
else return false; // 重複
}
TreeNode node = new TreeNode(data);
node.parent = parent;
if (data < parent.data) parent.left = node;
else parent.right = node;
size++;
return true;
}
/** 查找 */
public TreeNode findTreeNode(Integer data) {
TreeNode cur = root;
while (cur != null) {
if (data < cur.data) cur = cur.left;
else if (data > cur.data) cur = cur.right;
else return cur;
}
return null;
}
// 刪除操作見下文完整代碼...
}
| 遍歷順序 | 遞歸版 | 迭代版關鍵點 |
|---|---|---|
| 前序 (根-左-右) | 打印 → 遞歸左 → 遞歸右 | 棧壓入右再左,彈出即根序 |
| 中序 (左-根-右) | 遞歸左 → 打印 → 遞歸右 | 沿左鏈入棧,彈出打印,轉右 |
| 後序 (左-右-根) | 遞歸左 → 遞歸右 → 打印 | 雙棧法或單棧 + prev 指針判斷右子已訪問 |
public static void medOrderIter(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) { stack.push(cur); cur = cur.left; }
cur = stack.pop();
System.out.print(cur.data + " ");
cur = cur.right;
}
}
public static void postOrderIter(TreeNode root) {
if (root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root, prev = null;
while (cur != null || !stack.isEmpty()) {
while (cur != null) { stack.push(cur); cur = cur.left; }
cur = stack.peek();
if (cur.right == null || cur.right == prev) {
System.out.print(cur.data + " ");
stack.pop();
prev = cur;
cur = null;
} else {
cur = cur.right;
}
}
}
public boolean delete(Integer data) {
TreeNode node = findTreeNode(data);
if (node == null) return false;
// 情況 1 & 2:至多一個子節點
TreeNode child = (node.left != null) ? node.left : node.right;
if (child != null) {
child.parent = node.parent;
if (node.parent == null) root = child;
else if (node == node.parent.left) node.parent.left = child;
else node.parent.right = child;
} else { // 葉子
if (node.parent == null) root = null;
else if (node == node.parent.left) node.parent.left = null;
else node.parent.right = null;
}
// 情況 3:雙子節點
if (node.left != null && node.right != null) {
TreeNode pred = node.left;
while (pred.right != null) pred = pred.right; // 中序前驅
node.data = pred.data; // 複製值
return delete(pred.data); // 遞歸刪除前驅 (必為情況 1/2)
}
size--;
return true;
}
public class SearchTreeTest {
public static void main(String[] args) {
SearchTree tree = new SearchTree();
int[] data = {50, 80, 20, 60, 10, 30, 70, 90, 100, 40};
for (int v : data) tree.addTreeNode(v);
System.out.println("=== 遞歸前序 ==="); TreeOrder.preOrderRec(tree.root);
System.out.println("\n=== 迭代前序 ==="); TreeOrder.preOrderIter(tree.root);
System.out.println("\n=== 遞歸中序 (應有序) ==="); TreeOrder.inOrderRec(tree.root);
System.out.println("\n=== 迭代中序 ==="); TreeOrder.inOrderIter(tree.root);
System.out.println("\n=== 遞歸後序 ==="); TreeOrder.postOrderRec(tree.root);
System.out.println("\n=== 迭代後序 ==="); TreeOrder.postOrderIter(tree.root);
System.out.println("\n查找 100: " + tree.findTreeNode(100));
System.out.println("查找 999: " + tree.findTreeNode(999));
tree.delete(20); // 刪除有雙子節點
System.out.println("\n刪除 20 後中序:"); TreeOrder.inOrderIter(tree.root);
}
}
遞歸中序 (應有序): 10 20 30 40 50 60 70 80 90 100
迭代中序: 10 20 30 40 50 60 70 80 90 100
查找 100: TreeNode[100]
查找 999: null
刪除 20 後中序: 10 30 40 50 60 70 80 90 100
| 操作 | 平均 | 最壞 (退化鏈表) | 備註 |
|---|---|---|---|
| 插入/查找/刪除 | O(log n) | O(n) | 需自平衡樹 (AVL/紅黑) 保證最壞 O(log n) |
| 中序遍歷 | O(n) | O(n) | 輸出有序序列 |
| 空間 | O(n) | O(n) | 遞歸深度 O(h),迭代顯式棧 O(h) |
生產建議:直接使用 java.util.TreeMap / TreeSet (紅黑樹實現),除非教學、面試或極特定定製需求。