typedef struct BSTNode {
int data;
struct BSTNode *left;
struct BSTNode *right;
} BSTNode, *BSTree;
遞歸結構體定義,`typedef` 簡化指針聲明。
void bst_insert(BSTree *root, int val) {
if (*root == NULL) {
*root = (BSTree)malloc(sizeof(BSTNode));
(*root)->data = val;
(*root)->left = (*root)->right = NULL;
return;
}
if (val < (*root)->data) {
bst_insert(&(*root)->left, val);
} else if (val > (*root)->data) {
bst_insert(&(*root)->right, val);
}
// 相等值不插入(集合語義,唯一性)
}
關鍵點:傳遞指針的指針(`BSTree *`),以便修改呼叫者的指針變數(根節點或子樹指針)。
// 前序:根 → 左 → 右
void preorder(BSTree root) {
if (root) {
printf("%d ", root->data);
preorder(root->left);
preorder(root->right);
}
}
// 中序:左 → 根 → 右(產生有序序列)
void inorder(BSTree root) {
if (root) {
inorder(root->left);
printf("%d ", root->data);
inorder(root->right);
}
}
// 後序:左 → 右 → 根(適用於銷毀、釋放)
void postorder(BSTree root) {
if (root) {
postorder(root->left);
postorder(root->right);
printf("%d ", root->data);
}
}
void bst_destroy(BSTree root) {
if (root) {
bst_destroy(root->left);
bst_destroy(root->right);
free(root);
}
}
必須後序:先釋放子樹,再釋放根,避免野指針。
BSTree bst_search(BSTree root, int key) {
while (root) {
if (key == root->data) return root;
root = (key < root->data) ? root->left : root->right;
}
return NULL;
}
int main() {
BSTree root = NULL;
int keys[] = {9, 4, 15, 6, 12, 17, 2};
int n = sizeof(keys) / sizeof(keys[0]);
for (int i = 0; i < n; i++) bst_insert(&root, keys[i]);
printf("前序: "); preorder(root); printf("\n");
printf("中序: "); inorder(root); printf("\n");
printf("後序: "); postorder(root); printf("\n");
// 查找測試
int target = 12;
BSTree found = bst_search(root, target);
printf("查找 %d: %s\n", target, found ? "存在" : "不存在");
bst_destroy(root);
return 0;
}
前序: 9 4 2 6 15 12 17 中序: 2 4 6 9 12 15 17 後序: 2 6 4 12 17 15 9 查找 12: 存在
9
/ \
4 15
/ \ / \
2 6 12 17
作者文末提出經典問題:統計 C 關鍵字在代碼中出現頻率。若用線性結構(數組/鏈表),查找/插入均為 O(n);BST 平均 O(log n),大規模關鍵字統計效能差異巨大。這正是「選擇正確資料結構」的核心動機。