AVL树详解及其C语言实现

目录

原理

旋转操作

应用场景

C语言实现

总结


原理

AVL树的全称是Adelson-Velsky and Landis Tree,简称AVL树。它是由苏联计算机科学家G.M. Adelson-Velsky和E.M. Landis在1962年发明的一种自平衡二叉搜索树。AVL树是一种自平衡的二叉搜索树,其特点是在每次插入或删除操作之后,都会自动调整树的结构,以确保树的高度保持在尽可能小的状态。这种调整是通过旋转操作来实现的,以保持树的平衡性。在AVL树中,每个节点都维护了一个平衡因子,即其左子树的高度减去右子树的高度。平衡因子的值只能为-1、0或1。当插入或删除操作导致某个节点的平衡因子超出这个范围时,就需要进行旋转操作来恢复平衡。保证树的高度始终为O(log n),从而实现了O(log n)时间复杂度的查找、插入和删除操作。

平衡因子(Balance Factor)

  • 定义:平衡因子 = 左子树高度 - 右子树高度

  • AVL树要求每个节点的平衡因子为 -1、0、1,否则触发旋转调整。

旋转操作包括单旋转和双旋转两种。单旋转分为左旋和右旋,用于处理只有一个子树不平衡的情况。双旋转则是先进行一次左旋或右旋,再进行一次右旋或左旋,用于处理两个子树都不平衡的情况。通过旋转操作,可以确保AVL树在任何时候都保持平衡,从而保证了查找、插入和删除操作的高效性。

旋转操作

1. 右旋(LL型失衡)

失衡结构(左子树更高):
    A (BF=2)
   /
  B (BF=1)
 /
C

右旋后:
  B
 / \
C   A

2. 左旋(RR型失衡)

失衡结构(右子树更高):
A (BF=-2)
 \
  B (BF=-1)
   \
    C

左旋后:
  B
 / \
A   C

3. 左右旋(LR型失衡)

失衡结构:
  A (BF=2)
 /
B (BF=-1)
 \
  C

先左旋B-C,再右旋A-C:
  C
 / \
B   A

4. 右左旋(RL型失衡)

失衡结构:
A (BF=-2)
 \
  B (BF=1)
 /
C

先右旋B-C,再左旋A-C:
  C
 / \
A   B

应用场景

  1. 数据库索引:需要快速查找且数据频繁更新。

  2. 内存高效字典:如C++的 std::map(红黑树更常用,但AVL树查询更快)。

  3. 实时系统:保证最坏情况下操作时间稳定。

优缺点分析

优点 缺点
严格平衡,查询效率高(O(log n)) 插入/删除频繁时旋转开销大
适合读多写少场景 实现复杂度高于普通二叉搜索树
内存占用与普通二叉树相同 相比红黑树,维护平衡更频繁

C语言实现

1. 定义AVL树节点

#include 
#include 

typedef struct AVLNode {
    int key;
    int height;
    struct AVLNode *left;
    struct AVLNode *right;
} AVLNode;

// 计算节点高度
int height(AVLNode* node) {
    return (node == NULL) ? 0 : node->height;
}

// 计算平衡因子
int balanceFactor(AVLNode* node) {
    return height(node->left) - height(node->right);
}

// 更新节点高度
void updateHeight(AVLNode* node) {
    int hl = height(node->left);
    int hr = height(node->right);
    node->height = (hl > hr ? hl : hr) + 1;
}

2. 旋转操作实现

// 右旋(处理LL型失衡)
AVLNode* rightRotate(AVLNode* y) {
    AVLNode* x = y->left;
    AVLNode* T2 = x->right;

    x->right = y;
    y->left = T2;

    updateHeight(y);
    updateHeight(x);
    return x;
}

// 左旋(处理RR型失衡)
AVLNode* leftRotate(AVLNode* x) {
    AVLNode* y = x->right;
    AVLNode* T2 = y->left;

    y->left = x;
    x->right = T2;

    updateHeight(x);
    updateHeight(y);
    return y;
}

// 平衡调整(根据失衡类型选择旋转)
AVLNode* balance(AVLNode* node) {
    int bf = balanceFactor(node);

    // LL型失衡 → 右旋
    if (bf > 1 && balanceFactor(node->left) >= 0)
        return rightRotate(node);

    // RR型失衡 → 左旋
    if (bf < -1 && balanceFactor(node->right) <= 0)
        return leftRotate(node);

    // LR型失衡 → 先左旋左子树,再右旋
    if (bf > 1 && balanceFactor(node->left) < 0) {
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    // RL型失衡 → 先右旋右子树,再左旋
    if (bf < -1 && balanceFactor(node->right) > 0) {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node; // 无需调整
}

3. 插入操作

AVLNode* insert(AVLNode* node, int key) {
    if (node == NULL) {
        AVLNode* newNode = (AVLNode*)malloc(sizeof(AVLNode));
        newNode->key = key;
        newNode->height = 1;
        newNode->left = newNode->right = NULL;
        return newNode;
    }

    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node; // 不允许重复键

    updateHeight(node);
    return balance(node); // 插入后调整平衡
}

4. 删除操作

// 找到最小节点(用于删除有两个子节点的节点)
AVLNode* minValueNode(AVLNode* node) {
    while (node->left != NULL)
        node = node->left;
    return node;
}

AVLNode* deleteNode(AVLNode* root, int key) {
    if (root == NULL) return root;

    if (key < root->key)
        root->left = deleteNode(root->left, key);
    else if (key > root->key)
        root->right = deleteNode(root->right, key);
    else {
        // 节点有一个子节点或无子节点
        if (root->left == NULL || root->right == NULL) {
            AVLNode* temp = root->left ? root->left : root->right;
            if (temp == NULL) {
                temp = root;
                root = NULL;
            } else {
                *root = *temp; // 复制内容
            }
            free(temp);
        } else {
            // 节点有两个子节点:用右子树最小节点替代
            AVLNode* temp = minValueNode(root->right);
            root->key = temp->key;
            root->right = deleteNode(root->right, temp->key);
        }
    }

    if (root == NULL) return root;

    updateHeight(root);
    return balance(root); // 删除后调整平衡
}

5. 遍历与测试代码

// 中序遍历(验证排序性质)
void inOrder(AVLNode* root) {
    if (root != NULL) {
        inOrder(root->left);
        printf("%d (BF=%d) ", root->key, balanceFactor(root));
        inOrder(root->right);
    }
}

int main() {
    AVLNode* root = NULL;

    // 插入测试
    root = insert(root, 10);
    root = insert(root, 20);
    root = insert(root, 30); // 触发RR型失衡 → 左旋
    root = insert(root, 5);
    root = insert(root, 15);
    root = insert(root, 25); // 触发RL型失衡 → 右旋+左旋

    printf("中序遍历结果: ");
    inOrder(root); // 输出: 5 (BF=0) 10 (BF=0) 15 (BF=0) 20 (BF=0) 25 (BF=0) 30 (BF=0)
    printf("\n");

    // 删除测试
    root = deleteNode(root, 20);
    printf("删除20后的遍历: ");
    inOrder(root); // 输出: 5 (BF=0) 10 (BF=1) 15 (BF=0) 25 (BF=0) 30 (BF=0)
    printf("\n");

    return 0;
}

总结

  • 核心机制:通过四种旋转保持平衡因子在 [-1, 1] 之间。

  • 适用场景:读操作远多于写操作,且需要稳定查询性能(如数据库索引)。

  • 实现要点:插入/删除后递归调整平衡,旋转操作修复失衡。

你可能感兴趣的:(C/C++,数据结构与算法,数据结构,c语言,算法)