• 平衡二叉树(AVL树)


    一、定义

    平衡二叉树,又称AVL树,它是一种特殊的二叉排序树。AVL树或者是一棵空树,或者是具有以下性质的二叉树:

    (1)左子树和右子树都是平衡二叉树;

    (2)左子树和右子树的深度(高度)之差的绝对值不超过1。

    二、AVL树的C++实现

    1、结点的定义

    class AVLNode
    {
    public:
        int key;            //结点的值
        int height;         //结点的高度,根结点为0
        AVLNode* left;      //左孩子
        AVLNode* right;     //右孩子
    
        /*构造函数*/
        AVLNode(int k, AVLNode* left, AVLNode* right) :key(k), height(0), left(left), right(right) {}
    };

    2、AVL树的操作

    AVL树同二叉排序树一样,有遍历(先序、中序、后序),最大值与最小值,插入和删除,销毁二叉树等操作,除插入和删除与二叉排序树操作不同之外,其余均与二叉排序树相同,所以这里就只写AVL插入和删除操作。

    class AVLTree
    {
    private:
        AVLNode* root;        //根节点
    public:
        /*构造函数*/
        AVLTree() :root(NULL) {};
    
        /*返回根节点*/
        AVLNode* getRoot() { return root; }
    
        /*先序遍历*/
        void preOrder(AVLNode* root);
    
        /*中序遍历*/
        void inOrder(AVLNode* root);
    
        /*后序遍历*/
        void postOrder(AVLNode* root);
    
        /*在AVL树root中查找值为key的结点并返回该结点*/
        AVLNode* search(AVLNode* root, int key);
    
        /*在AVL树中查找最小值结点并返回*/
        AVLNode* minimus(AVLNode* node);
    
        /*在AVL树中查找最大值结点并返回*/
        AVLNode* maximus(AVLNode* node);
    
        /*返回结点的高度*/
        int height(AVLNode* node);
    
        /*左左旋转*/
        AVLNode* leftLeftRotate(AVLNode* root);
    
        /*右右旋转*/
        AVLNode* rightRightRotate(AVLNode* root);
    
        /*左右旋转*/
        AVLNode* leftRightRotate(AVLNode* root);
    
        /*右左旋转*/
        AVLNode* rightLeftRotate(AVLNode* root);
    
        /*插入结点*/
        AVLNode* insert(AVLNode* root, int key);
    
        /*删除结点node*/
        AVLNode* deleteNode(AVLNode* root, AVLNode* node);
    
        /*销毁AVL树*/
        void destroy(AVLNode* root);
    };

    3、旋转

    在进行插入和删除之前需要先了解AVL树的旋转操作。旋转操作主要包括LL(左左)旋转、LR(左右)旋转、RR(右右)旋转、RL(右左)旋转,LL旋转与RR旋转对称,LR旋转与RL旋转对称。旋转操作是在插入结点或删除结点导致原AVL树不平衡时进行的。我的理解是当二叉树失衡的原因出现在“最低失衡根结点左子树的左子树”(所谓“最低失衡根结点”,则是从新增结点开始向根部回溯,所遇到的第一个失衡的根节点)时,则使用LL旋转来调整;当失衡出现在“最低失衡根节点左子树的右子树”,则使用LR旋转调整;RR旋转,RL旋转同理。具体的定义和操作可以看skywang12345的的文章:AVL树(二)之 C++的实现(我的这篇文章就是基于此文章,为了加深印象,在这里把实现再写一遍,加一些自己的理解)。

    3.1 LL旋转

    如上图所示,找到“最低失衡根结点”,上图是结点5,二叉树失衡的原因是因为结点1的存在,而结点1位于结点5“左子树的左子树”,所以要使用LL旋转来调节,只需一次旋转即可达到平衡。具体的方法是:LL旋转的对象是“最低失衡根结点”,也就是结点5,找到5的左孩子3,将3的右孩子4变成5的左孩子,最后将5变成3的右孩子,调整后的AVL树如下所示:

    具体代码:

    /*LL旋转,
    * 参数: 
    * root : 失衡AVL树根节点
    * 返回值 : 调整后的AVL树根节点
    */
    AVLNode* AVLTree::leftLeftRotate(AVLNode* root)
    {
        AVLNode* lchild = root->left;
        root->left = lchild->right;
        lchild->right = root;
    
        lchild->height = max(height(lchild->left), height(root)) + 1;
        root->height = max(height(root->left), height(root->right)) + 1;
    
        return lchild;
    }

     3.2 RR旋转

    RR旋转与LL旋转对称。

    如上图所示,“最低失衡根结点”是结点2,二叉树的失衡是结点6导致的,而结点6位于结点2“右子树的右子树”,所以要使用RR旋转来调节,只需一次旋转即可达到平衡。方法与LL旋转类似:RR旋转的对象是“最低失衡根结点”,这里是结点2,找到2的右孩子4,将4的左孩子3变成2的右孩子,最后将2变成4的右孩子,旋转后的结果如下图所示:

    RR旋转代码如下:

    /*RR旋转,
    * 参数:
    * root : 失衡AVL树根节点
    * 返回值 : 调整后的AVL树根节点
    */
    AVLNode* AVLTree::rightRightRotate(AVLNode* root)
    {
        AVLNode* rchild = root->right;
        root->right = rchild->left;
        rchild->left = root;
    
        rchild->height = max(height(root), height(rchild->right)) + 1;
        root->height = max(height(root->left), height(root->right)) + 1;
    
        return rchild;
    }

     3.3 LR旋转

    LL旋转和RR旋转只需一次旋转即可达到平衡,而LR旋转和RL旋转需两次旋转才能达到平衡。

     如上图所示,“最低失衡根结点”为结点5,二叉树失衡是因为结点3的存在,结点3位于结点5“左子树的右子树”,所以使用LR旋转来调节。方法:(1)先对最低失衡根结点的左孩子(结点2)进行RR旋转;(2)再对最低失衡根结点(结点5)进行LL旋转。下图演示了调整过程。

    LR代码如下:

    /*LR旋转
    * 参数:
    * root : 失衡AVL树根节点
    * 返回值 : 调整后的AVL树根节点
    */
    AVLNode* AVLTree::leftRightRotate(AVLNode* root)
    {
        root->left = rightRightRotate(root->left);    //先对左子树右右旋转
        return leftLeftRotate(root);    //再对根结点左左旋转
    }

    3.4 RL旋转

    RL旋转与LR旋转对称,先LL旋转,在RR旋转。

    分析过程与LR相似。旋转步骤:(1)先对最低失衡结点右孩子(结点5)LL旋转;(2)在对最低失衡结点(结点2)RR旋转。旋转过程如下:

    RL实现代码:

    /*RL旋转
    * 参数:
    * root : 失衡AVL树根节点
    * 返回值 : 调整后的AVL树根节点
    */
    AVLNode* AVLTree::rightLeftRotate(AVLNode* root)
    {
        root->right = leftLeftRotate(root->right);
        return rightRightRotate(root);
    }

    4、插入结点与删除结点

    4.1 插入结点

    插入操作与向二叉排序树中插入大体相同,只是多了插入结点后判断二叉树是否失衡以及失衡后的调整操作。

    /*
    * 将结点插入到AVL树中,并返回根节点
    *
    * 参数说明:
    *     root 插入新结点前AVL树的根结点
    *     key 插入的结点的键值
    * 返回值:
    *     插入结点后AVL树的根节点
    */
    AVLNode* AVLTree::insert(AVLNode* root, int key)
    {
        if (root == NULL)
            root = new AVLNode(key, NULL, NULL);
        else if (key < root->key)    //插入左子树
        {
            root->left = insert(root->left, key);
            if (height(root->left) - height(root->right) == 2)    //插入导致二叉树失衡
            {
                if (key < root->left->key)
                    root = leftLeftRotate(root);
                else root = leftRightRotate(root);
            }
        }
        else if (key>root->key)        //插入右子树
        {
            root->right = insert(root->right, key);
            if (height(root->right) - height(root->left) == 2)    //插入导致二叉树失衡
            {
                if (key > root->right->key)
                    root = rightRightRotate(root);
                else root = rightLeftRotate(root);
            }
        }
        root->height = max(height(root->left), height(root->right)) + 1;
        return root;
    }

    4.2 删除结点

    删除结点后要判断二叉树是否失衡,若失衡则进行调整操作。

    /*
    * 将结点插入到AVL树中,并返回根节点
    *
    * 参数说明:
    *     root 删除结点前AVL树的根结点
    *     node 要删除的结点
    * 返回值:
    *     删除结点node后AVL树的根节点
    */
    AVLNode* AVLTree::deleteNode(AVLNode* root, AVLNode* node)
    {
        if (root == NULL)
            return NULL;
    
        if (node->key < root->key)        //要删除的结点在左子树
        {
            root->left = deleteNode(root->left, node);
            if (height(root->right) - height(root->left) == 2)    //删除导致二叉树失衡
            {
                AVLNode* rightNode = root->right;
                if (height(rightNode->left)>height(rightNode->right))
                    root = rightLeftRotate(root);
                else root = rightRightRotate(root);
            }
        }
        else if (node->key > root->key)    //要删除的结点在右子树
        {
            root->right = deleteNode(root->right, node);
            if (height(root->left) - height(root->right) == 2)    //删除导致二叉树失衡
            {
                AVLNode* leftNode = root->left;
                if (height(leftNode->left) > height(leftNode->right))
                    root = leftLeftRotate(root);
                else root = leftRightRotate(root);
            }
        }
        else    //找到了要删除的结点
        {
            if (root->left != NULL&&root->right != NULL)    //结点的左右子树均不为空
            {
                if (height(root->left) > height(root->right))
                {
                    /*
                    * 如果tree的左子树比右子树高;
                    * 则(01)找出tree的左子树中的最大节点
                    *  (02)将该最大节点的值赋值给tree。
                    *  (03)删除该最大节点。
                    * 这类似于用"tree的左子树中最大节点"做"tree"的替身;
                    * 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。
                    */
    
                    AVLNode* maxNode = maximus(root->left);
                    root->key = maxNode->key;
                    root->left = deleteNode(root->left, maxNode);
                }
                else
                {
                    /*
                     * 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
                     * 则(01)找出tree的右子树中的最小节点
                     *  (02)将该最小节点的值赋值给tree。
                     *  (03)删除该最小节点。
                     * 这类似于用"tree的右子树中最小节点"做"tree"的替身;
                     * 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。
                     */
    
                    AVLNode* minNode = minimus(root->right);
                    root->key = minNode->key;
                    root->right = deleteNode(root->right, minNode);
                }
            }
            else
            {
                AVLNode* tmp = root;
                root = (root->left != NULL) ? root->left : root->right;
                delete tmp;
            }
        }
        return root;
    }

    三、测试代码

    1、头文件 avltree.h

      1 #include <algorithm>
      2 #include <iostream>
      3 #include <cstdio>
      4 #include <cstring>
      5 using namespace std;
      6 
      7 class AVLNode
      8 {
      9 public:
     10     int key;            //结点的值
     11     int height;            //结点的高度,根结点为0
     12     AVLNode* left;        //左孩子
     13     AVLNode* right;        //右孩子
     14 
     15     /*构造函数*/
     16     AVLNode(int k, AVLNode* left, AVLNode* right) :key(k), height(0), left(left), right(right) {}
     17 };
     18 
     19 class AVLTree
     20 {
     21 private:
     22     AVLNode* root;        //根节点
     23 public:
     24     /*构造函数*/
     25     AVLTree() :root(NULL) {};
     26 
     27     /*返回根节点*/
     28     AVLNode* getRoot() { return root; }
     29 
     30     /*先序遍历*/
     31     void preOrder(AVLNode* root);
     32 
     33     /*中序遍历*/
     34     void inOrder(AVLNode* root);
     35 
     36     /*后序遍历*/
     37     void postOrder(AVLNode* root);
     38 
     39     /*在AVL树root中查找值为key的结点并返回该结点*/
     40     AVLNode* search(AVLNode* root, int key);
     41 
     42     /*在AVL树中查找最小值结点并返回*/
     43     AVLNode* minimus(AVLNode* node);
     44 
     45     /*在AVL树中查找最大值结点并返回*/
     46     AVLNode* maximus(AVLNode* node);
     47 
     48     /*返回结点的高度*/
     49     int height(AVLNode* node);
     50 
     51     /*左左旋转*/
     52     AVLNode* leftLeftRotate(AVLNode* root);
     53 
     54     /*右右旋转*/
     55     AVLNode* rightRightRotate(AVLNode* root);
     56 
     57     /*左右旋转*/
     58     AVLNode* leftRightRotate(AVLNode* root);
     59 
     60     /*右左旋转*/
     61     AVLNode* rightLeftRotate(AVLNode* root);
     62 
     63     /*插入结点*/
     64     AVLNode* insert(AVLNode* root, int key);
     65 
     66     /*删除结点node*/
     67     AVLNode* deleteNode(AVLNode* root, AVLNode* node);
     68 
     69     /*销毁AVL树*/
     70     void destroy(AVLNode* root);
     71 };
     72 
     73 /*先序遍历*/
     74 void AVLTree::preOrder(AVLNode* root)
     75 {
     76     if (root == NULL)
     77         return;
     78     cout << root->key << " ";
     79     preOrder(root->left);
     80     preOrder(root->right);
     81 }
     82 
     83 /*中序遍历*/
     84 void AVLTree::inOrder(AVLNode* root)
     85 {
     86     if (root == NULL)
     87         return;
     88     inOrder(root->left);
     89     cout << root->key << " ";
     90     inOrder(root->right);
     91 }
     92 
     93 /*后序遍历*/
     94 void AVLTree::postOrder(AVLNode* root)
     95 {
     96     if (root == NULL)
     97         return;
     98     postOrder(root->left);
     99     postOrder(root->right);
    100     cout << root->key << " ";
    101 }
    102 
    103 /*在AVL树root中查找值为key的结点并返回该结点*/
    104 AVLNode* AVLTree::search(AVLNode* root, int key)
    105 {
    106     if (root == NULL || root->key == key)
    107         return root;
    108     if (key < root->key)
    109         search(root->left, key);
    110     else search(root->right, key);
    111 }
    112 
    113 /*在AVL树中查找最小值结点并返回*/
    114 AVLNode* AVLTree::minimus(AVLNode* node)
    115 {
    116     if (node->left == NULL)
    117         return node;
    118     return minimus(node->left);
    119 }
    120 
    121 /*在AVL树中查找最大值结点并返回*/
    122 AVLNode* AVLTree::maximus(AVLNode* node)
    123 {
    124     if (node->right == NULL)
    125         return node;
    126     return maximus(node);
    127 }
    128 
    129 /*返回结点的高度*/
    130 int AVLTree::height(AVLNode* node)
    131 {
    132     if (node != NULL)
    133         return node->height;
    134     return 0;
    135 }
    136 
    137 
    138 /*LL旋转,
    139 * 参数: 
    140 * root : 失衡AVL树根节点
    141 * 返回值 : 调整后的AVL树根节点
    142 */
    143 AVLNode* AVLTree::leftLeftRotate(AVLNode* root)
    144 {
    145     AVLNode* lchild = root->left;
    146     root->left = lchild->right;
    147     lchild->right = root;
    148 
    149     lchild->height = max(height(lchild->left), height(root)) + 1;
    150     root->height = max(height(root->left), height(root->right)) + 1;
    151 
    152     return lchild;
    153 }
    154 
    155 /*RR旋转
    156 * 参数:
    157 * root : 失衡AVL树根节点
    158 * 返回值 : 调整后的AVL树根节点
    159 */
    160 AVLNode* AVLTree::rightRightRotate(AVLNode* root)
    161 {
    162     AVLNode* rchild = root->right;
    163     root->right = rchild->left;
    164     rchild->left = root;
    165 
    166     rchild->height = max(height(root), height(rchild->right)) + 1;
    167     root->height = max(height(root->left), height(root->right)) + 1;
    168 
    169     return rchild;
    170 }
    171 
    172 /*LR旋转
    173 * 参数:
    174 * root : 失衡AVL树根节点
    175 * 返回值 : 调整后的AVL树根节点
    176 */
    177 AVLNode* AVLTree::leftRightRotate(AVLNode* root)
    178 {
    179     root->left = rightRightRotate(root->left);    //先对左子树右右旋转
    180     return leftLeftRotate(root);    //再对根结点左左旋转
    181 }
    182 
    183 /*RL旋转
    184 * 参数:
    185 * root : 失衡AVL树根节点
    186 * 返回值 : 调整后的AVL树根节点
    187 */
    188 AVLNode* AVLTree::rightLeftRotate(AVLNode* root)
    189 {
    190     root->right = leftLeftRotate(root->right);
    191     return rightRightRotate(root);
    192 }
    193 
    194 /*
    195 * 将结点插入到AVL树中,并返回根节点
    196 *
    197 * 参数说明:
    198 *     root 插入新结点前AVL树的根结点
    199 *     key 插入的结点的键值
    200 * 返回值:
    201 *     插入结点后AVL树的根节点
    202 */
    203 AVLNode* AVLTree::insert(AVLNode* root, int key)
    204 {
    205     if (root == NULL)
    206         root = new AVLNode(key, NULL, NULL);
    207     else if (key < root->key)    //插入左子树
    208     {
    209         root->left = insert(root->left, key);
    210         if (height(root->left) - height(root->right) == 2)    //插入二叉树导致失衡
    211         {
    212             if (key < root->left->key)
    213                 root = leftLeftRotate(root);
    214             else root = leftRightRotate(root);
    215         }
    216     }
    217     else if (key>root->key)        //插入右子树
    218     {
    219         root->right = insert(root->right, key);
    220         if (height(root->right) - height(root->left) == 2)    //插入导致二叉树失衡
    221         {
    222             if (key > root->right->key)
    223                 root = rightRightRotate(root);
    224             else root = rightLeftRotate(root);
    225         }
    226     }
    227     root->height = max(height(root->left), height(root->right)) + 1;
    228     return root;
    229 }
    230 
    231 /*
    232 * 将结点插入到AVL树中,并返回根节点
    233 *
    234 * 参数说明:
    235 *     root 删除结点前AVL树的根结点
    236 *     node 要删除的结点
    237 * 返回值:
    238 *     删除结点node后AVL树的根节点
    239 */
    240 AVLNode* AVLTree::deleteNode(AVLNode* root, AVLNode* node)
    241 {
    242     if (root == NULL)
    243         return NULL;
    244 
    245     if (node->key < root->key)        //要删除的结点在左子树
    246     {
    247         root->left = deleteNode(root->left, node);
    248         if (height(root->right) - height(root->left) == 2)    //删除导致二叉树失衡
    249         {
    250             AVLNode* rightNode = root->right;
    251             if (height(rightNode->left)>height(rightNode->right))
    252                 root = rightLeftRotate(root);
    253             else root = rightRightRotate(root);
    254         }
    255     }
    256     else if (node->key > root->key)    //要删除的结点在右子树
    257     {
    258         root->right = deleteNode(root->right, node);
    259         if (height(root->left) - height(root->right) == 2)    //删除导致二叉树失衡
    260         {
    261             AVLNode* leftNode = root->left;
    262             if (height(leftNode->left) > height(leftNode->right))
    263                 root = leftLeftRotate(root);
    264             else root = leftRightRotate(root);
    265         }
    266     }
    267     else    //找到了要删除的结点
    268     {
    269         if (root->left != NULL&&root->right != NULL)    //结点的左右子树均不为空
    270         {
    271             if (height(root->left) > height(root->right))
    272             {
    273                 /*
    274                 * 如果tree的左子树比右子树高;
    275                 * 则(01)找出tree的左子树中的最大节点
    276                 *  (02)将该最大节点的值赋值给tree。
    277                 *  (03)删除该最大节点。
    278                 * 这类似于用"tree的左子树中最大节点"做"tree"的替身;
    279                 * 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。
    280                 */
    281 
    282                 AVLNode* maxNode = maximus(root->left);
    283                 root->key = maxNode->key;
    284                 root->left = deleteNode(root->left, maxNode);
    285             }
    286             else
    287             {
    288                 /*
    289                  * 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
    290                  * 则(01)找出tree的右子树中的最小节点
    291                  *  (02)将该最小节点的值赋值给tree。
    292                  *  (03)删除该最小节点。
    293                  * 这类似于用"tree的右子树中最小节点"做"tree"的替身;
    294                  * 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。
    295                  */
    296 
    297                 AVLNode* minNode = minimus(root->right);
    298                 root->key = minNode->key;
    299                 root->right = deleteNode(root->right, minNode);
    300             }
    301         }
    302         else
    303         {
    304             AVLNode* tmp = root;
    305             root = (root->left != NULL) ? root->left : root->right;
    306             delete tmp;
    307         }
    308     }
    309     return root;
    310 }
    311 
    312 /*销毁二叉树*/
    313 void AVLTree::destroy(AVLNode* root)
    314 {
    315     if (root == NULL)
    316         return;
    317     destroy(root->left);
    318     destroy(root->right);
    319     delete root;
    320 }
    查看代码

    2、源文件avltree.cpp

     1 #include "avltree.h"
     2 
     3 int main()
     4 {
     5     int a[] = { 3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9 };
     6     int len = sizeof(a) / sizeof(a[0]);
     7 
     8     AVLTree* avlTree = new AVLTree();
     9     AVLNode* root = avlTree->getRoot();
    10     for (int i = 0;i < len;i++)
    11         root = avlTree->insert(root, a[i]);
    12     
    13     cout << "先序遍历:";
    14     avlTree->preOrder(root);
    15     cout << endl;
    16 
    17     cout << "中序遍历:";
    18     avlTree->inOrder(root);
    19     cout << endl;
    20 
    21     cout << "后序遍历:";
    22     avlTree->postOrder(root);
    23     cout << endl;
    24 
    25     cout << "删除结点4" << endl;
    26     AVLNode* node = avlTree->search(root, 4);
    27     if (node != NULL)
    28         AVLNode* dnode = avlTree->deleteNode(root, node);
    29 
    30     cout << "删除结点4后先序遍历:";
    31     avlTree->preOrder(root);
    32     cout << endl;
    33     cout << "删除结点4后中序遍历:";
    34     avlTree->inOrder(root);
    35     cout << endl;
    36 
    37     cout << "销毁AVL树" << endl;
    38     avlTree->destroy(root);
    39     return 0;
    40 }

    3、运行结果

    先序遍历:7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 
    中序遍历:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
    后序遍历:1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 
    删除结点4
    删除结点4后先序遍历:7 5 2 1 3 6 13 11 9 8 10 12 15 14 16 
    删除结点4后中序遍历:1 2 3 5 6 7 8 9 10 11 12 13 14 15 16 
    销毁AVL树

    四、小工具

    这里分享一个二叉排序树的可视化小工具,来自http://www.cnblogs.com/bbvi/p/5104916.html。

    五、参考

    1、http://www.cnblogs.com/skywang12345/p/3577360.html

  • 相关阅读:
    common-pool2连接池详解与使用
    Nginx实现页面缓存
    Nginx实现动静分离
    lnmp环境下nginx配置‘负载均衡’
    用phpstudy搭建的lnmp环境下mysql授权远程连接
    新浪OAuth网络登录,请求access_token时遇到21323的错误
    使用curl进行模拟登录
    yii2.0分页
    yii2.0表单自带验证码
    yii2.0查询关联数据以及widgets小部件
  • 原文地址:https://www.cnblogs.com/sench/p/7786718.html
Copyright © 2020-2023  润新知