AVL 樹(Adelson-Velsky and Landis)是最早發明的自平衡二元搜尋樹,透過維護每個節點的平衡因子(Balance Factor, BF = 左子樹高度 - 右子樹高度)在 {-1, 0, 1} 範圍內,保證最壞情況 O(log n) 的查詢、插入、刪除效能。本文聚焦刪除操作,這是 AVL 樹實現中最複雜、最易出錯的部分。
typedef enum { LH = -1, EH = 0, RH = 1 } BalanceFactor;
typedef struct AVLNode {
int key;
struct AVLNode *lchild, *rchild, *parent;
BalanceFactor bf;
int height; // 可選,bf 可推導
} AVLNode;
typedef struct { AVLNode *root; } AVLTree;
// 對外介面
int avl_delete(AVLTree *tree, int key) {
bool lower = false;
if (!tree->root) return AVL_SUCCESS;
return avl_search_and_delete(tree, tree->root, key, &lower);
}
// 內部遞迴:搜尋並刪除
int avl_search_and_delete(AVLTree *tree, AVLNode *node, int key, bool *lower) {
if (key < node->key) { // 往左
if (!node->lchild) return AVL_SUCCESS;
avl_search_and_delete(tree, node->lchild, key, lower);
if (*lower) return avl_delete_left_balance(tree, node, lower);
} else if (key > node->key) { // 往右
if (!node->rchild) return AVL_SUCCESS;
avl_search_and_delete(tree, node->rchild, key, lower);
if (*lower) return avl_delete_right_balance(tree, node, lower);
} else { // 找到目標節點
return avl_delete_node(tree, node, lower);
}
return AVL_SUCCESS;
}
int avl_delete_node(AVLTree *tree, AVLNode *node, bool *lower) {
AVLNode *parent = node->parent;
// 情況 1 & 2:至多一個子樹
if (!node->rchild) {
*lower = true;
avl_replace_child(tree, parent, node, node->lchild);
free(node);
return AVL_SUCCESS;
}
if (!node->lchild) {
*lower = true;
avl_replace_child(tree, parent, node, node->rchild);
free(node);
return AVL_SUCCESS;
}
// 情況 3:雙子樹 —— 找左子樹最右節點替換
return avl_replace_and_delete(tree, node, node->lchild, lower);
}
// 遞迴找左子樹最右節點
int avl_replace_and_delete(AVLTree *tree, AVLNode *delNode, AVLNode *rNode, bool *lower) {
if (!rNode->rchild) { // 找到最右節點
*lower = true;
delNode->key = rNode->key; // 複製鍵值
AVLNode *rParent = rNode->parent;
if (rNode == delNode->lchild) {
avl_set_lchild(delNode, rNode->lchild);
} else {
avl_set_rchild(rParent, rNode->lchild);
}
free(rNode);
return AVL_SUCCESS;
}
// 繼續往右找
avl_replace_and_delete(tree, delNode, rNode->rchild, lower);
if (*lower) return avl_delete_right_balance(tree, rNode, lower);
return AVL_SUCCESS;
}
刪除導致子樹高度降低,祖先節點 BF 可能變為 ±2,需旋轉恢復。分左側降低(avl_delete_left_balance)與右側降低(avl_delete_right_balance)對稱處理。
| 右子樹 BF | 類型 | 旋轉 | 新 BF 設定 |
|---|---|---|---|
| RH (1) | RR | 左單旋 | node→EH, rchild→EH, lower=true |
| EH (0) | RR | 左單旋 | node→RH, rchild→LH, lower=false |
| LH (-1) | RL | 右左雙旋 | 視 rlchild BF 設定,lower=true |
int avl_delete_left_balance(AVLTree *tree, AVLNode *node, bool *lower) {
AVLNode *r = node->rchild, *rl = r->lchild, *p = node->parent;
switch (node->bf) {
case LH: node->bf = EH; *lower = true; break;
case EH: node->bf = RH; *lower = false; break;
case RH: // 失衡
switch (r->bf) {
case RH: case EH: // RR
if (r->bf == EH) { r->bf = LH; node->bf = RH; *lower = false; }
else { r->bf = EH; node->bf = EH; *lower = true; }
avl_rotate_left(tree, node);
break;
case LH: // RL
*lower = true;
switch (rl->bf) {
case LH: node->bf = EH; r->bf = RH; break;
case EH: node->bf = EH; r->bf = EH; break;
case RH: node->bf = LH; r->bf = EH; break;
}
rl->bf = EH;
avl_rotate_right(tree, r);
avl_rotate_left(tree, node);
break;
}
break;
}
return AVL_SUCCESS;
}
完全對稱,程式碼見原文 avl_delete_right_balance。
// 以 newChild 取代 parent 下的 oldChild
void avl_replace_child(AVLTree *tree, AVLNode *parent, AVLNode *oldChild, AVLNode *newChild) {
if (!parent) tree->root = newChild;
else if (parent->lchild == oldChild) parent->lchild = newChild;
else parent->rchild = newChild;
if (newChild) newChild->parent = parent;
}
// 左旋
void avl_rotate_left(AVLTree *tree, AVLNode *x) {
AVLNode *y = x->rchild;
avl_set_rchild(x, y->lchild);
avl_set_lchild(y, x);
avl_replace_child(tree, x->parent, x, y);
}
// 右旋對稱
void avl_assert(AVLNode *node) {
if (!node || !node->parent) return;
assert(node->parent->lchild == node || node->parent->rchild == node);
assert(node->parent != node->lchild && node->parent != node->rchild);
}
// 在關鍵操作後呼叫,開發階段啟用,發布關閉 NDEBUG
對包含 1000 個隨機鍵的 AVL 樹,隨機刪除 800 個,驗證:
結果:通過。刪除操作完整保持 AVL 性質。
lower 布林旗標傳遞「子樹高度是否降低」,驅動祖先節點平衡檢查。avl_assert 可在開發期捕獲 90% 以上指針錯誤。