首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

演算法導論:AVL 樹刪除操作完整實現與平衡調整詳解

     編輯:關於C

前言

AVL 樹(Adelson-Velsky and Landis)是最早發明的自平衡二元搜尋樹,透過維護每個節點的平衡因子(Balance Factor, BF = 左子樹高度 - 右子樹高度)在 {-1, 0, 1} 範圍內,保證最壞情況 O(log n) 的查詢、插入、刪除效能。本文聚焦刪除操作,這是 AVL 樹實現中最複雜、最易出錯的部分。

刪除節點的三種基本情況

  1. 葉子節點:直接移除,向上回溯更新高度並檢查平衡。
  2. 單一子樹(僅左或僅右):用子樹根節點取代被刪節點,向上回溯。
  3. 雙子樹皆存在:找左子樹最右節點(前驅)或右子樹最左節點(後繼)取代被刪節點的鍵值,然後刪除該前驅/後繼節點(必屬情況 1 或 2),向上回溯。

關鍵資料結構

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)對稱處理。

左子樹降低(右高 RH=2)

右子樹 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;
}

右子樹降低(左高 LH=-2)

完全對稱,程式碼見原文 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 個,驗證:

  • 中序遍歷仍為有序序列。
  • 所有節點 BF ∈ {-1,0,1}。
  • 樹高 ≈ 1.44 * log2(N)。

結果:通過。刪除操作完整保持 AVL 性質。

總結

  1. 刪除核心是三種情況分派 + 遞迴回溯更新 BF + 四種旋轉修復。
  2. 雙子樹情況用前驅/後繼節點鍵值替換轉化為單/無子樹刪除,避免複雜指針重組。
  3. lower 布林旗標傳遞「子樹高度是否降低」,驅動祖先節點平衡檢查。
  4. 嚴謹的 avl_assert 可在開發期捕獲 90% 以上指針錯誤。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved