基於平衡二叉樹(AVL Tree)實現動態查找表,包含三大核心功能:
typedef struct AVLNode {
KeyType key; // 關鍵字
InfoType data; // 附加數據
int bf; // 平衡因子:-1, 0, +1
struct AVLNode *lchild, *rchild;
} AVLNode, *AVLTree;
| 情況 | 平衡因子變化 | 旋轉類型 | 代碼關鍵 |
|---|---|---|---|
| LL | 根 bf=+2, 左子樹 bf=+1 | 右單旋轉 | R_Rotate |
| RR | 根 bf=-2, 右子樹 bf=-1 | 左單旋轉 | L_Rotate |
| LR | 根 bf=+2, 左子樹 bf=-1 | 先左後右雙旋轉 | L_Rotate(p->lchild); R_Rotate(p); |
| RL | 根 bf=-2, 右子樹 bf=+1 | 先右後左雙旋轉 | R_Rotate(p->rchild); L_Rotate(p); |
// 返回值:是否導致樹高增加
bool InsertAVL(AVLTree *T, KeyType key, InfoType data, bool *taller) {
if (*T == NULL) {
*T = (AVLTree)malloc(sizeof(AVLNode));
(*T)->key = key; (*T)->data = data; (*T)->bf = 0;
(*T)->lchild = (*T)->rchild = NULL;
*taller = true;
return true;
}
if (key == (*T)->key) return false; // 已存在
if (key < (*T)->key) {
if (!InsertAVL(&(*T)->lchild, key, data, taller)) return false;
if (*taller) {
switch ((*T)->bf) {
case 1: LeftBalance(T); *taller = false; break;
case 0: (*T)->bf = 1; *taller = true; break;
case -1: (*T)->bf = 0; *taller = false; break;
}
}
} else { // key > (*T)->key
if (!InsertAVL(&(*T)->rchild, key, data, taller)) return false;
if (*taller) {
switch ((*T)->bf) {
case -1: RightBalance(T); *taller = false; break;
case 0: (*T)->bf = -1; *taller = true; break;
case 1: (*T)->bf = 0; *taller = false; break;
}
}
}
return true;
}
思路:中序遍歷較小樹,逐個插入較大樹。時間複雜度 O(m log n)(m ≤ n)。優化:將兩樹轉為有序數組,歸併後重建平衡樹,複雜度 O(m+n)。
遞歸分裂函數 SplitAVL(T, key, *T1, *T2):
void SplitAVL(AVLTree T, KeyType key, AVLTree *T1, AVLTree *T2) {
if (T == NULL) { *T1 = *T2 = NULL; return; }
if (T->key <= key) {
SplitAVL(T->rchild, key, &T->rchild, T2);
*T1 = T;
// 此處需更新 T1 平衡因子,可能需旋轉
} else {
SplitAVL(T->lchild, key, T1, &T->lchild);
*T2 = T;
// 同理更新 T2
}
}
難點在於分裂過程中保持兩棵結果樹的平衡性,通常需引入輔助函數在回溯時重新平衡。
taller/shorter 標誌位至關重要。InOrderTraverse 驗證有序性,CheckBalance 遞歸校驗所有節點 |bf| ≤ 1。DestroyAVL 遞歸釋放所有節點。