AVL树的详细实现(C++)

AVL树概念

前面已经介绍了二叉搜索树,但是二叉搜索树在某些情况下会出现极度不平衡,其树形结构便退化成了链表,查找效率也会下降。于是出现了 AVL 树,AVL 树保证了二叉搜索树的平衡,引入了平衡监督机制,也就是在插入结点时,树中某一部分不平衡度超过一个阈值后将出发平衡调整操作,这样便保证了树的平衡度在可接受的范围内,最大的好处就是提高了搜索的效率。

AVL 树是一种平衡二叉树,其名字来源于发明者的名字(Adelson-Velskii 以及 Landis)。AVL树的递归定义如下:

  1. 左右子树的高度差小于等于 1。
  2. 其每一个子树均为平衡二叉树。

通过这两个性质就可以判定一棵树是否为平衡二叉树了。

如下图就是一颗平衡二叉树,任何节点的两个子树的高度最大差别为 1:

AVL树的详细实现-图1

而下图不是一颗平衡二叉树,因为结点 7 的两颗子树高度相差为 2。

AVL树的详细实现-图2

AVL 树的查找、插入和删除在平均和最坏情况下均为 O(logn)。

如果在 AVL 树种插入或删除节点后,使得高度之差大于 1。此时,AVL 树的平衡状态就被破坏,不再是平衡二叉树,为了维持一个平衡状态,需要对其进行旋转处理。

平衡二叉树加入了“平衡因子”概念,定义为:

某个结点的左子树的高度减去右子树的高度得到的差值。AVL 树的所有结点的平衡因子的绝对值都不超过 1。

为了计算平衡因子,自然需要在节点种引入高度这一属性。高度一般定义为左右子树高度的最大值。下面将会详细介绍 AVL 树的详细实现。

AVL树的失衡调整

失衡

在 AVL 树中进行插入或删除节点后,可能导致 AVL 树失去平衡。这种失衡可以概括为 4 中姿态:

(1)LL 失衡:LeftLeft,也称为“左左”。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致“根的左子树的高度”比“根的右子树的高度”大 2,导致 AVL 树失去了平衡。如下图:

AVL树的详细实现-图3
(2)LR 失衡:LeftRight,也称为“左右”。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致“根的左子树的高度”比“根的右子树的高度”大 2,导致 AVL 树失去了平衡。如下图:

AVL树的详细实现-图4

(3)RL 失衡:RightLeft,称为“右左”。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致“根的右子树的高度”比“根的左子树的高度”大 2,导致 AVL 树失去了平衡。

AVL树的详细实现-图5

(4)RR 失衡:RightRight,称为“右右”。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致“根的右子树的高度”比“根的左子树的高度”大 2,导致 AVL 树失去了平衡。

AVL树的详细实现-图6

旋转

LL 旋转

LL 失衡的情况,可以通过一次左旋转将 AVL 树恢复平衡,如下图:

AVL树的详细实现-图7

可以发现,只通过一次旋转就可以恢复为 AVL 树。对于 LL 旋转,可以这样理解:

LL 旋转是围绕“失去平衡的 AVL 根节点”进行的,也就是节点 k2,由于是 LL 情况,即“左左情况”,用手抓着“左孩子,即 k1”使劲向左摇,将 k1 变成了根节点,而 k2 变成了 k1 的右子树,而“k1 的右子树”变成了“k2 的左子树”。

代码如下:

AVLTreeNode<T> *leftLeftRotation(AVLTreeNode<T> *&k2)
{
    AVLTreeNode<T> *k1 = k2->m_leftChild;
    k2->m_leftChild = k1->m_rightNode;
    k1->m_rightNode = k2;
    k2->m_height = std::max(height(k2->m_leftChild), height(k2->m_rightNode)) + 1;
    k1->m_height = std::max(height(k1->m_leftChild), k2->m_height) + 1;
    return k1;
}
RR 旋转

理解了 LL 之后,RR 旋转就相当容易理解了。RR 就是与 LL 堆成的情况,RR 恢复平衡的旋转方法如下:

AVL树的详细实现-图8

RR 旋转也只需要一次即可。对于 RR 旋转,可以这样理解:

RR 旋转是围绕“失去平衡的 AVL 根节点”进行的,也就是节点 k1,由于是 RR 情况,即“右右情况”,用手抓着“右孩子,即 k2”使劲向右摇,将 k2 变成了根节点,而 k1 变成了 k2 的左子树,而“k1 的右子树”变成了“k2 的左子树”。

代码如下:

AVLTreeNode<T> *rightRightRotation(AVLTreeNode<T> *&k1)
{
    AVLTreeNode<T> *k2 = k1->m_rightNode;
    k1->m_rightNode = k2->m_leftChild;
    k2->m_leftChild = k1;

    k1->m_height = std::max(height(k1->m_leftChild), height(k1->m_rightNode)) + 1;
    k2->m_height = std::max(height(k2->m_rightNode), k1->m_height) + 1;
    return k2;
}
LR 旋转

LR 失衡情况,需要经过两次旋转才能让 AVL 树恢复平衡。如下图:

AVL树的详细实现-图9

第一次旋转是围绕 k1 进行的 RR 旋转,第二次旋转是围绕 k3 进行的 LL 旋转。

代码如下:

AVLTreeNode<T> *leftRightRotation(AVLTreeNode<T> *&k3)
{
    k3->m_leftChild = rightRightRotation(k3->m_leftChild);
    return leftLeftRotation(k3);
}
RL 旋转

RL 旋转是和 LR 对称的情况,RL 恢复平衡的旋转方法如下:

AVL树的详细实现-图10

第一次旋转是围绕 k3 进行的 LL 旋转,第二次是围绕 k1 进行的 RR 旋转。

代码如下:

AVLTreeNode<T> *rightLeftRotation(AVLTreeNode<T> *&k1)
{
    k1->m_rightNode = leftLeftRotation(k1->m_rightNode);
    return rightRightRotation(k1);
}

AVL树的实现

节点定义

template <typename T>
struct AVLTreeNode
{
    T m_key;                  // 关键字
    int m_height;             // 高度
    AVLTreeNode *m_leftChild; // 左孩子
    AVLTreeNode *m_rightNode; // 右孩子
    AVLTreeNode(T value, AVLTreeNode *l, AVLTreeNode *r) : m_key(value), m_height(0), m_leftChild(l), m_rightNode(r) {}
};

详细代码实现

注意:关于 AVL 树的各种二叉树的通用接口之前的二叉树篇幅中已经实现过,这里不再赘述,仅实现了 AVL 树中重要的接口。

#include <iostream>
using namespace std;

template <typename T>
struct AVLTreeNode
{
    T m_key;                  // 关键字
    int m_height;             // 高度
    AVLTreeNode *m_leftChild; // 左孩子
    AVLTreeNode *m_rightNode; // 右孩子
    AVLTreeNode(T value, AVLTreeNode *l, AVLTreeNode *r) : m_key(value), m_height(0), m_leftChild(l), m_rightNode(r) {}
};

template <typename T>
class AVLTree
{
public:
    AVLTree() : m_root(NULL) {}
    ~AVLTree() { destroy(m_root); }

public:
    // 中序遍历
    void inOrder() { inOrder(m_root); }

    // 先序遍历
    void preOrder() { preOrder(m_root); }

    // 树的高度
    int height() { return height(m_root); }

    // 查找AVL树种值为key的结点(递归)
    AVLTreeNode<T> *search(T key) { return search(m_root, key); }

    // 查找AVL树种值为key的结点(非递归)
    AVLTreeNode<T> *iterativeSearch(T key) { return iterativeSearch(m_root, key); }

    // 查找最小结点,返回最小结点的键值
    T minimum()
    {
        AVLTreeNode<T> *p = minimum(m_root);
        if (p != NULL)
            return p->m_key;
        return (T)NULL;
    }

    // 查找最大结点,返回最大结点的键值
    T maximum()
    {
        AVLTreeNode<T> *p = maximum(m_root);
        if (p != NULL)
            return p->m_key;
        return (T)NULL;
    }

    // 将键值为key的结点插入到AVL中
    void insert(T key) { insert(m_root, key); }

    // 删除键值为key的结点
    void remove(T key)
    {
        AVLTreeNode<T> *z = search(m_root, key);
        if (z != NULL)
            m_root = remove(m_root, z);
    }

    // 销毁AVL树
    void destroy() { destroy(m_root); }

private:
    void inOrder(AVLTreeNode<T> *node)
    {
        if (node == NULL)
            return;
        inOrder(node->m_leftChild);
        cout << node->m_key << " ";
        inOrder(node->m_rightNode);
    }

    void preOrder(AVLTreeNode<T> *node)
    {
        if (node == NULL)
            return;
        cout << node->m_key << " ";
        preOrder(node->m_leftChild);
        preOrder(node->m_rightNode);
    }

    int height(AVLTreeNode<T> *node)
    {
        return node != NULL ? node->m_height : 0;
    }

    void destroy(AVLTreeNode<T> *&tree)
    {
        if (tree == NULL)
            return;
        if (tree->m_leftChild != NULL)
            destroy(tree->m_leftChild);
        if (tree->m_rightNode != NULL)
            destroy(tree->m_rightNode);
        delete tree;
    }

    AVLTreeNode<T> *search(AVLTreeNode<T> *node, T key)
    {
        if (node == NULL || node->m_key == key)
            return node;
        if (key < node->m_key)
            return search(node->m_leftChild, key);
        else
            return search(node->m_rightNode, key);
    }

    AVLTreeNode<T> *iterativeSearch(AVLTreeNode<T> *node, T key)
    {
        while ((node != NULL) && (node->m_key != key))
        {
            if (key < node->m_key)
                node = node->m_leftChild;
            else
                node = node->m_rightNode;
        }
        return node;
    }

    AVLTreeNode<T> *minimum(AVLTreeNode<T> *node)
    {
        if (node == NULL)
            return NULL;
        while (node->m_leftChild != NULL)
        {
            node = node->m_leftChild;
        }
        return node;
    }

    AVLTreeNode<T> *maximum(AVLTreeNode<T> *node)
    {
        if (node == NULL)
            return NULL;
        while (node->m_rightNode != NULL)
        {
            node = node->m_rightNode;
        }
        return node;
    }

    AVLTreeNode<T> *insert(AVLTreeNode<T> *&node, T key)
    {
        if (node == NULL)
        {
            node = new AVLTreeNode<T>(key, NULL, NULL);
        }
        else if (key < node->m_key) // key插入node的左子树的情况
        {
            node->m_leftChild = insert(node->m_leftChild, key);
            // 插入节点后,如果AVL树失衡,需要进行相应调节
            if (height(node->m_leftChild) - height(node->m_rightNode) == 2)
            {
                if (key < node->m_leftChild->m_key)
                    node = leftLeftRotation(node);
                else
                    node = leftRightRotation(node);
            }
        }
        else if (key > node->m_key) // key插入node的右子树的情况
        {
            node->m_rightNode = insert(node->m_rightNode, key);
            // 插入节点后,如果AVL树失衡,需要进行相应调节
            if (height(node->m_rightNode) - height(node->m_leftChild) == 2)
            {
                if (key > node->m_rightNode->m_key)
                    node = rightRightRotation(node);
                else
                    node = rightLeftRotation(node);
            }
        }
        else
        {
            cout << "添加失败,不能添加相同的结点" << endl;
        }
        node->m_height = max(height(node->m_leftChild), height(node->m_rightNode)) + 1;
        return node;
    }

    AVLTreeNode<T> *remove(AVLTreeNode<T> *&node, AVLTreeNode<T> *z)
    {
        if (node == NULL || z == NULL)
            return NULL;

        if (z->m_key < node->m_key) // 待删除的节点在tree的左子树中
        {
            node->m_leftChild = remove(node->m_leftChild, z);
            // 删除节点后,如果AVL树失衡,需要进行相应调节
            if (height(node->m_rightNode) - height(node->m_leftChild) == 2)
            {
                AVLTreeNode<T> *r = node->m_rightNode;
                if (height(r->m_leftChild) > height(r->m_rightNode))
                    node = rightLeftRotation(node);
                else
                    node = rightRightRotation(node);
            }
        }
        else if (z->m_key > node->m_key) //待删除的节点在node的右子树中
        {
            // 删除节点后,如果AVL树失衡,需要进行相应调节
            node->m_rightNode = remove(node->m_rightNode, z);
            if (height(node->m_leftChild) - height(node->m_rightNode) == 2)
            {
                AVLTreeNode<T> *l = node->m_leftChild;
                if (height(l->m_rightNode) > height(l->m_leftChild))
                    node = leftRightRotation(node);
                else
                    node = leftLeftRotation(node);
            }
        }
        else //当前node就是要删除的节点
        {
            if ((node->m_leftChild != NULL) && (node->m_rightNode != NULL))
            {
                if (height(node->m_leftChild) > height(node->m_rightNode))
                {
                    if (height(node->m_leftChild) > height(node->m_rightNode))
                    {
                        /* 如果node节点的左子树比右子树高,则:
                         * (01)找出node的左子树中最大节点
                         * (02)将该最大节点的值赋值给node
                         * (03)删除该最大节点
                         * 相当于用node的左子树中最大节点作为node的替身
                         * 这种方式删除node左子树中最大节点之后,AVL树任然是平衡的
                         */
                        AVLTreeNode<T> *max = maximum(node->m_leftChild);
                        node->m_key = max->m_key;
                        node->m_leftChild = remove(node->m_leftChild, max);
                    }
                    else
                    {
                        /* 如果node节点的右子树比左子树高,则:
                         * (01)找出node的右子树中最小节点
                         * (02)将该最小节点的值赋值给node
                         * (03)删除该最小节点
                         * 相当于用node的右子树中最小节点作为node的替身
                         * 这种方式删除node右子树中最小节点之后,AVL树任然是平衡的
                         */
                        AVLTreeNode<T> *min = minimum(node->m_rightNode);
                        node->m_key = min->m_key;
                        node->m_rightNode = remove(node->m_rightNode, min);
                    }
                }
            }
            else
            {
                // 被删除的节点等于node,并且node有一个孩子
                // 将当前节点指向该孩子节点并删除当前节点
                AVLTreeNode<T> *tmp = node;
                node = node->m_leftChild != NULL ? node->m_leftChild : node->m_rightNode;
                delete tmp;
            }
        }
        return node;
    }

private:
    /* LL:左子树的左边失去平衡(左单旋转)
     *       k2              k1     
     *      /  \            /  \
     *     k1   z   ===>   x    k2
     *    /  \                 /  \
     *   x    y               y    z
     */

    AVLTreeNode<T> *leftLeftRotation(AVLTreeNode<T> *&k2)
    {
        AVLTreeNode<T> *k1 = k2->m_leftChild;
        k2->m_leftChild = k1->m_rightNode;
        k1->m_rightNode = k2;
        k2->m_height = std::max(height(k2->m_leftChild), height(k2->m_rightNode)) + 1;
        k1->m_height = std::max(height(k1->m_leftChild), k2->m_height) + 1;
        return k1;
    }

    /* RR:右子树的右边失去平衡(右单旋转)
     *       k1              k2     
     *      /  \            /  \
     *     x   k2   ===>   k1   z
     *        /  \        /  \   
     *       y    z      x    y  
     */
    AVLTreeNode<T> *rightRightRotation(AVLTreeNode<T> *&k1)
    {
        AVLTreeNode<T> *k2 = k1->m_rightNode;
        k1->m_rightNode = k2->m_leftChild;
        k2->m_leftChild = k1;

        k1->m_height = std::max(height(k1->m_leftChild), height(k1->m_rightNode)) + 1;
        k2->m_height = std::max(height(k2->m_rightNode), k1->m_height) + 1;
        return k2;
    }

    /* LR:左子树的右边失去平衡(左双旋转)
     *       k3               k3               k2
     *      /  \     RR      /  \     LL      /  \
     *     k1   D   ===>   k2    D   ===>   k1    k3
     *    /  \            /  \             /  \  /  \
     *   A    K2         k1   C           A    B C   D
     *       /  \       /  \
     *      B    C     A    B
     */
    AVLTreeNode<T> *leftRightRotation(AVLTreeNode<T> *&k3)
    {
        k3->m_leftChild = rightRightRotation(k3->m_leftChild);
        return leftLeftRotation(k3);
    }

    /* RL:右子树的左边失去平衡(右双旋转)
     *     k1               k1                K2
     *    /  \      LL     /  \      RR      /  \
     *   A    k3   ===>   A    k2   ===>   k1    K3
     *       /  \             /  \        /  \  /  \
     *      k2   D           B    k3     A    B C   D
     *     /  \                  /   \
     *    B    D                C     D
     */
    AVLTreeNode<T> *rightLeftRotation(AVLTreeNode<T> *&k1)
    {
        k1->m_rightNode = leftLeftRotation(k1->m_rightNode);
        return rightRightRotation(k1);
    }

private:
    AVLTreeNode<T> *m_root; // AVL树的根节点
};

int main()
{
    AVLTree<int> tree;
    tree.insert(3);
    tree.insert(2);
    tree.insert(1);
    tree.insert(4);
    tree.insert(5);
    tree.insert(6);
    tree.insert(7);

    tree.insert(16);
    tree.insert(15);
    tree.insert(14);
    tree.insert(13);
    tree.insert(12);
    tree.insert(11);
    tree.insert(10);
    tree.insert(8);
    tree.insert(9);

    tree.preOrder();
    cout << endl;
    tree.inOrder();
    cout << endl;

    tree.remove(8);
    tree.preOrder();
    cout << endl;
    tree.preOrder();
    cout << endl;

    return 0;
}

参考
https://wangkuiwu.github.io/2013/02/02/avltree-cpp

  • 15
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
AVL树是一种自平衡二叉搜索树,它的每个节点存储一个键值对,且每个节点的左子树和右子树的高度差不超过1。这种平衡特性使得AVL树在查找、插入和删除操作方面都有很好的性能表现。 下面是一个简单的AVL树C++实现: ```c++ #include <iostream> using namespace std; // AVL树节点 struct Node { int key; int height; Node *left; Node *right; Node(int k) : key(k), height(1), left(NULL), right(NULL) {} }; // 获取节点高度 int height(Node *node) { if (node == NULL) { return 0; } return node->height; } // 获取节点平衡因子 int balanceFactor(Node *node) { if (node == NULL) { return 0; } return height(node->left) - height(node->right); } // 更新节点高度 void updateHeight(Node *node) { node->height = max(height(node->left), height(node->right)) + 1; } // 右旋操作 Node* rightRotate(Node *node) { Node *leftChild = node->left; Node *rightChild = leftChild->right; leftChild->right = node; node->left = rightChild; updateHeight(node); updateHeight(leftChild); return leftChild; } // 左旋操作 Node* leftRotate(Node *node) { Node *rightChild = node->right; Node *leftChild = rightChild->left; rightChild->left = node; node->right = leftChild; updateHeight(node); updateHeight(rightChild); return rightChild; } // 插入节点 Node* insert(Node *node, int key) { if (node == NULL) { return new Node(key); } 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); int bf = balanceFactor(node); if (bf > 1) { if (balanceFactor(node->left) >= 0) { return rightRotate(node); } else { node->left = leftRotate(node->left); return rightRotate(node); } } else if (bf < -1) { if (balanceFactor(node->right) <= 0) { return leftRotate(node); } else { node->right = rightRotate(node->right); return leftRotate(node); } } return node; } // 中序遍历AVL树 void inOrder(Node *node) { if (node == NULL) { return; } inOrder(node->left); cout << node->key << " "; inOrder(node->right); } int main() { Node *root = NULL; root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); root = insert(root, 50); root = insert(root, 25); inOrder(root); cout << endl; return 0; } ``` 在上面的实现中,我们使用了递归插入节点,并在插入节点后更新了节点的高度和平衡因子。当节点的平衡因子大于1或小于-1时,我们进行相应的旋转操作来保持树的平衡。最后,我们在main函数中插入一些节点,并进行中序遍历来检查树是否正确构建。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

code_peak

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值