C++的数据结构(十):AVL树

        AVL树是一种自平衡的二叉搜索树,得名于其发明者G.M. Adelson-Velsky和E.M. Landis。在AVL树中,任何节点的两个子树的高度最多相差1,这种性质确保了AVL树的查找、插入和删除操作的时间复杂度接近O(log n)。

        AVL树是一种二叉搜索树,它通过调整节点间的结构来保持树的平衡。平衡因子(Balance Factor)是AVL树中一个关键的概念,定义为节点的左子树高度与右子树高度的差。在AVL树中,任何节点的平衡因子的绝对值不得超过1。如下图所示。

         AVL树通过旋转操作来保持树的平衡,AVL树在节点插入或删除后,若导致树不平衡,则会通过四种旋转操作之一来重新平衡树。这四种旋转操作分别是:左旋、右旋、左右旋和右左旋。

        1. 左旋操作通常发生在右子树比左子树高太多的时候。具体操作是:将当前节点(我们称其为T)的右子节点(我们称其为R)提升为新的根节点,将T节点降为R的左子节点,R的左子节点则成为T的右子节点。示例代码如下。

struct TreeNode {
    int val;
    int height;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int val) : val(val), height(1), left(nullptr), right(nullptr) {}
};

TreeNode* leftRotate(TreeNode* x) {
    TreeNode* y = x->right;  // y是x的右子节点
    x->right = y->left;      // y的左子节点成为x的右子节点
    y->left = x;            // x成为y的左子节点

    // 更新高度
    x->height = 1 + max(height(x->left), height(x->right));
    y->height = 1 + max(height(y->left), height(y->right));

    return y;  // 新的根节点
}

        2. 右旋操作与左旋相反,发生在节点不平衡,且平衡因子小于-1,同时左子节点的平衡因子小于等于0时。我们将左子节点提升为当前节点的父节点,当前节点变为左子节点的右子节点,完成右旋。示例代码如下。

TreeNode* rightRotate(TreeNode* y) {
    TreeNode* x = y->left;  // x是y的左子节点
    y->left = x->right;     // x的右子节点成为y的左子节点
    x->right = y;          // y成为x的右子节点

    // 更新高度
    x->height = 1 + max(height(x->left), height(x->right));
    y->height = 1 + max(height(y->left), height(y->right));

    return x;  // 新的根节点
}

         3. 左右旋是左旋和右旋的组合操作,发生在节点不平衡,且平衡因子大于1,同时右子节点的平衡因子小于0时。首先,对右子节点进行左旋,然后对整个树以当前节点为根进行右旋。

        4. 右左旋也是左旋和右旋的组合操作,但顺序相反。它发生在节点不平衡,且平衡因子小于-1,同时左子节点的平衡因子大于0时。首先,对左子节点进行右旋,然后对整个树以当前节点为根进行左旋。 

        在实际应用中,当我们对AVL树进行插入或删除操作时,一旦导致树不平衡,我们就需要根据节点及其子节点的平衡因子,决定执行哪种旋转操作,来恢复树的平衡。通过正确应用这四种旋转操作,我们可以确保AVL树始终保持平衡状态,从而保持高效的性能。

        需要注意的是,旋转操作只是AVL树维护平衡的一种方式,实际的应用场景中还需要处理其他情况,比如如何高效地查找节点、如何正确地插入和删除节点等。但无论何种情况,保持树的平衡都是AVL树设计的核心所在。

        AVL树由于具有良好的平衡性,常被用于需要高效查找、插入和删除操作的场景中,如数据缓存、关联数组、文件系统等。它提供了一种高效的存储和访问数据的方式,特别是在数据量大、操作频繁的情况下,AVL树能够保持较好的性能。

        下面是一个简单的AVL树的C++实现,包含插入、删除和打印树的函数。代码如下。

#include <iostream>
#include <stack>
#include <algorithm> 
using namespace std;

// 定义AVL树的节点结构体
struct AVLNode {
    int key;
    int height;
    AVLNode* left;
    AVLNode* right;

    AVLNode(int k) : key(k), height(1), left(nullptr), right(nullptr) {}
};

// 查找树中的最小值节点
AVLNode* minValueNode(AVLNode* node) {
    AVLNode* current = node;

    // 循环直到左孩子是nullptr
    while (current->left != nullptr)
        current = current->left;

    return current;
}


// 获取节点的高度
int getHeight(AVLNode* N) {
    if (N == nullptr)
        return 0;
    return N->height;
}

// 获取平衡因子
int getBalance(AVLNode* N) {
    if (N == nullptr)
        return 0;
    return getHeight(N->left) - getHeight(N->right);
}

// 更新节点高度
void updateHeight(AVLNode* N) {
    if (N != nullptr)
        N->height = 1 + max(getHeight(N->left), getHeight(N->right));
}

// 右旋
AVLNode* rightRotate(AVLNode* y) {
    AVLNode* x = y->left;
    AVLNode* T2 = x->right;

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

    updateHeight(y);
    updateHeight(x);

    return x;
}

// 左旋
AVLNode* leftRotate(AVLNode* x) {
    AVLNode* y = x->right;
    AVLNode* T2 = y->left;

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

    updateHeight(x);
    updateHeight(y);

    return y;
}

// 获取插入新节点后的平衡AVL树
AVLNode* insert(AVLNode* node, int key) {
    if (node == nullptr)
        return (new AVLNode(key));

    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else // Duplicate keys not allowed
        return node;

    // 更新高度
    updateHeight(node);

    // 获取平衡因子
    int balance = getBalance(node);

    // 左左情况
    if (balance > 1 && key < node->left->key)
        return rightRotate(node);

    // 右右情况
    if (balance < -1 && key > node->right->key)
        return leftRotate(node);

    // 左右情况
    if (balance > 1 && key > node->left->key) {
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    // 右左情况
    if (balance < -1 && key < node->right->key) {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node;
}

// 从AVL树中删除一个节点
AVLNode* deleteNode(AVLNode* root, int key) {
    if (root == nullptr)
        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 && root->right) {
            AVLNode* temp = minValueNode(root->right);
            root->key = temp->key;
            root->right = deleteNode(root->right, temp->key);
        }
        // 节点只有一个子节点或没有子节点
        else {
            AVLNode* temp = root->left ? root->left : root->right;
            if (temp == nullptr) {
                temp = root;
                root= nullptr;
            } else {
                *root = *temp;
            }
            free(temp);
        }
    }

    if (root == nullptr)
        return root;

    // 更新高度
    updateHeight(root);

    // 获取平衡因子
    int balance = getBalance(root);

    // 右右情况
    if (balance > 1 && getBalance(root->left) >= 0)
        return rightRotate(root);

    // 左左情况
    if (balance < -1 && getBalance(root->right) <= 0)
        return leftRotate(root);

    // 左右情况
    if (balance > 1 && getBalance(root->left) < 0) {
        root->left = leftRotate(root->left);
        return rightRotate(root);
    }

    // 右左情况
    if (balance < -1 && getBalance(root->right) > 0) {
        root->right = rightRotate(root->right);
        return leftRotate(root);
    }

    return root;
}

// 打印AVL树(中序遍历)
void inorder(AVLNode* root) {
    if (root != nullptr) {
        inorder(root->left);
        cout << root->key << " ";
        inorder(root->right);
    }
}

// 主函数测试
int main() {
    AVLNode* root = nullptr;

    // 插入节点
    root = insert(root, 10);
    insert(root, 20);
    insert(root, 30);
    insert(root, 40);
    insert(root, 50);
    insert(root, 25);

    // 打印AVL树
    cout << "所构建的AVL树的中序遍历是 \n";
    inorder(root);
    cout << endl;

    // 删除节点
    root = deleteNode(root, 25);

    // 再次打印AVL树
    cout << "删除后修改的AVL树的中序遍历为 \n";
    inorder(root);
    cout << endl;

    return 0;
}

        总的来说,AVL树是一种非常有用的数据结构,它通过自动平衡保证了高效的查找、插入和删除操作。掌握AVL树的原理和操作对于理解数据结构和算法的重要性以及提高编程技能都非常有帮助。

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈工程师Linda

感恩您的鼓励,我会继续创作作品

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值