平衡二叉查找树(AVL树)


AVL树的定义

平衡因子 : 树中某结点其左子树的高度和右子树的高度之差
AVL树中的任意一个结点, 其平衡因子绝对值小于2
AVL树是一种特殊的二叉搜索树 (BST树), 相对于数据极端情况下, 二叉搜索树会退化成为单链表, AVL树定义了旋转操作, 在平衡因子大于等于2时, AVL树会旋转来调整树的结构, 来重新满足平衡因子小于2

在这里插入图片描述

这两棵树, 右边的为AVL树

现在定义AVL树结构如下:

struct AVLNode 
{
    AVLNode() 
        : val(0), left(nullptr), right(nullptr) 
    {}
    AVLNode(int v)
        : val(v), left(nullptr), right(nullptr)
    {}
    int val;    //data
    // int height;  //当前结点高度
    AVLNode* left;
    AVLNode* right;
};

 
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

AVL树不平衡的情况

AVL树大部分操作都和BST树相同, 只有在插入删除结点时, 有可能造成AVL树失去平衡, 而且只有那些在被插入/删除结点到根节点的路径上的结点有可能出现失衡, 因为只有那些结点的子树结构发生了变化

当插入新结点导致不平衡时, 我们需要找到距离新节点最近的不平衡结点为轴来转动AVL树来达到平衡

左子树的左子树插入结点 (左左)

在这里插入图片描述
向该AVL树添加结点 1, 导致结点 6 失衡 ( 结点 2 相对于结点 6 为左子树的左子树), 那么就旋转结点 6, 使其平衡因子重新满足AVL树条件

//左左情况旋转(t是失衡结点)
    void LL(AVLNode** t) 
    {
        if (t != nullptr)
        {
            AVLNode* tmpPtr = (*t)->left;
            (*t)->left = tmpPtr->right;    //t左子树的右子树作为t的左子树
            tmpPtr->right = *t;
            *t = tmpPtr;
        }
    }

 
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

右子树的右子树插入节点 (右右)

在这里插入图片描述

 //右右情况旋转
    void RR(AVLNode** t)
    {
        if (t != nullptr) 
        {
            AVLNode* tmpPtr = (*t)->right;
            (*t)->right = tmpPtr->left;
            tmpPtr->left = *t;
            *t = tmpPtr;
        }
    }

 
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

左子树的右子树插入节点 (左右)

在这里插入图片描述
结点 8 的左子树的右子树位置插入结点 6, 结点 8 失衡, 这时需要先将失衡节点 8 的左子树进行"右右"情况旋转, 然后再对结点 8 进行"左左"情况旋转

//左右情况旋转 (t为失衡结点,新节点位于t的左子树的右子树)
    void LR(AVLNode** t) 
    {
        RR(&(*t)->left);
        LL(t);
    }

 
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

右子树的左子树插入节点 (右左)

在这里插入图片描述
插入结点 12 时失衡, 失衡结点 10 结点, 新结点是其右子树的右子树, 这时需要先将其右子树按"左左"情况向右旋转, 再按"右右"情况向左先旋转

扫描二维码关注公众号,回复: 13538974 查看本文章
//右左情况旋转
    void RL(AVLNode** t) 
    {
        LL(&(*t)->right);
        RR(t);
    }

 
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

删除结点

AVL树是一种特殊的二叉搜索树, 所以要考虑的情况和BST树删除结点一样, 不同的是删除一个结点有可能引起父结点失衡, 所以我们需要在每次回退的时候计算结点高度

关于二叉搜索树具体如何删除结点上一篇博客介绍了

//找到左子树中最大值结点
int findMaxKeyInLef(AVLNode* node) 
    {
        if (node == nullptr)
            return 0;
        else if (node->right == nullptr)
            return node->val;
        return findMaxKeyInLef(node->right);
    }
    AVLNode* delNodeFromTree(AVLNode** node, int val) 
    {
        if (node == nullptr) 
            return nullptr;
        else if (val < (*node)->val)
        {
            (*node)->left = delNodeFromTree(&(*node)->left, val);
            //判断是否失衡,删了左子树一个结点,所以判断右子树高度是否过高
            if ((getHeight((*node)->right) - getHeight((*node)->left)) > 1) 
                //右子树的左子树高度比右子树的右子树更高,相当于给右子树的右子树插入了新节点,相当于"右右"情况
                if (getHeight((*node)->right->left) > getHeight((*node)->right->right))
                    RL(node);
                else 
                    RR(node);
            return (*node);
        }
        else if (val > (*node)->val)
        {
            (*node)->right = delNodeFromTree(&(*node)->right, val);
            //判断是否失衡,删了右子树一个结点,所以判断左子树高度是否过高
            if ((getHeight((*node)->left) - getHeight((*node)->right)) > 1) 
                //左子树的左子树高度比右子树的右子树更高,相当于给左子树的左子树插入了新节点,相当于"左左"情况
                if (getHeight((*node)->left->left) > getHeight((*node)->left->right))
                    LL(node);
                else 
                    LR(node);
            return (*node);
        }
        else if (val == (*node)->val)
        {
            //如果是叶子节点
            if ((*node)->left == nullptr && (*node)->right == nullptr) 
            {
                delete (*node);
                (*node) = nullptr;
                return (*node);;
            }
            //如果左子树非空,将右子树续接到父节点
            else if ((*node)->left != nullptr) 
            {
                AVLNode* tmp = (*node)->left;
                delete (*node);
                return tmp;
            }
            //如果右子树非空,将左子树续接到父节点
            else if ((*node)->right != nullptr) 
            {
                AVLNode* tmp = (*node)->right;
                delete (*node);
                return tmp;
            }
            //左右子树皆非空
            else 
            {
                //寻找左子树中最大节点,即左子树中最右节点
                //(也可以寻找右子树中最小节点,即右子树中最左节点)
                int maxVal = findMaxKeyInLef((*node)->left);
                //交换这两个节点
                (*node)->val = maxVal;
                //删除那个用来交换的节点
                (*node)->left = delNodeFromTree(&(*node)->left, maxVal);
                return *node;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

插入节点

 //插入结点
    void insertNode(AVLNode** t, int v) 
    {   
        //插入结点,使用二级指针改变父节点左右子树指针指向
        if (*t == nullptr) 
            *t = new AVLNode(v); 
        else if (v < (*t)->val) 
        {
            insertNode(&((*t)->left), v);
            int leftH = getHeight((*t)->left);
            int rightH = getHeight((*t)->right);
            //插入到左子树,肯定是左子树高度更高,判断这时平衡因子是否大于1
            if ((leftH - rightH) > 1) 
            {
                if (v < (*t)->left->val) 
                    LL(t);
                else
                    LR(t);
            }
        }
        else if (v > (*t)->val) 
        {
            insertNode(&((*t)->right), v);
            int leftH = getHeight((*t)->left);
            int rightH = getHeight((*t)->right);
            if ((rightH - leftH) > 1) 
            {
                if (v > (*t)->right->val)
                    RR(t);
                else 
                    RL(t);
            }
        }
        else 
            return ;
    }

 
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

更复杂的情况

在这里插入图片描述

当我们插入结点 6, 结点 10 失衡, 但第一眼看上去这是"左左左" ? 难道无法处理这种情况吗?
其实这时我们上面给出的"左左"情况下旋转的代码中:

  (*t)->left = tmpPtr->right;    //t左子树的右子树作为t的左子树

 
  
  
  • 1

就发挥了作用了

所有代码

只做演示用, 没有写出析构

#include <queue>
#include <vector>
#include <iostream>
using namespace std;

struct AVLNode
{
AVLNode()
: val(0), left(nullptr), right(nullptr)
{}
AVLNode(int v)
: val(v), left(nullptr), right(nullptr)
{}
int val; //data
// int height; //当前结点高度
AVLNode* left;
AVLNode* right;
};

class AVLTree
{
public:
AVLTree() : root(nullptr)
{}
~AVLTree()
{}

//查找某特定值结点
AVLNode* findNode(int val) 
{
    return findNodeInTree(root, val);
}

//插入结点,t为插入节点
void insert(int v) 
{   
    insertNode(&amp;root, v);
}

//删除结点,val是待删除结点data
void delNode(int val) 
{
    delNodeFromTree(&amp;root, val);
}

//层次遍历
void traverse()
{
    if (root != nullptr) 
    {
        queue&lt;AVLNode *&gt; q;
        q.push(root);
        AVLNode* tmpPtr;
        while (!q.empty()) 
        {
            tmpPtr = q.front();
            q.pop();
            cout &lt;&lt; tmpPtr-&gt;val &lt;&lt; ' ';

            if (tmpPtr-&gt;left != nullptr)
                q.push(tmpPtr-&gt;left);
            if (tmpPtr-&gt;right != nullptr) 
                q.push(tmpPtr-&gt;right);
        }
        cout &lt;&lt; endl;
    }
}

//求结点所在高度(只有一个根结点时高度为1)
int getHeight(AVLNode* t) 
{
    int leftHeight, rightHeight;
    if (t != nullptr) 
    {
        leftHeight = getHeight(t-&gt;left);
        rightHeight = getHeight(t-&gt;right);

        return (leftHeight &gt; rightHeight) ? 
            (leftHeight + 1) : (rightHeight + 1);
    }
    else 
        return 0;
}

private:
//左左情况旋转(t是失衡结点)
void LL(AVLNode** t)
{
if (t != nullptr)
{
AVLNode* tmpPtr = (*t)->left;
(t)->left = tmpPtr->right; //t左子树的右子树作为t的左子树
tmpPtr->right = t;
t = tmpPtr;
}
}
//右右情况旋转
void RR(AVLNode
t)
{
if (t != nullptr)
{
AVLNode
tmpPtr = (*t)->right;
(t)->right = tmpPtr->left;
tmpPtr->left = t;
t = tmpPtr;
}
}
//左右情况旋转 (t为失衡结点,新节点位于t的左子树的右子树)
void LR(AVLNode
t)
{
RR(&(t)->left);
LL(t);
}
//右左情况旋转
void RL(AVLNode
t)
{
LL(&(*t)->right);
RR(t);
}

//插入结点
void insertNode(AVLNode** t, int v) 
{   
    //插入结点,使用二级指针改变父节点左右子树指针指向
    if (*t == nullptr) 
        *t = new AVLNode(v); 
    else if (v &lt; (*t)-&gt;val) 
    {
        insertNode(&amp;((*t)-&gt;left), v);
        int leftH = getHeight((*t)-&gt;left);
        int rightH = getHeight((*t)-&gt;right);
        //插入到左子树,肯定是左子树高度更高,判断这时平衡因子是否大于1
        if ((leftH - rightH) &gt; 1) 
        {
            if (v &lt; (*t)-&gt;left-&gt;val) 
                LL(t);
            else
                LR(t);
        }
    }
    else if (v &gt; (*t)-&gt;val) 
    {
        insertNode(&amp;((*t)-&gt;right), v);
        int leftH = getHeight((*t)-&gt;left);
        int rightH = getHeight((*t)-&gt;right);
        if ((rightH - leftH) &gt; 1) 
        {
            if (v &gt; (*t)-&gt;right-&gt;val)
                RR(t);
            else 
                RL(t);
        }
    }
    else 
        return ;
}

AVLNode* findNodeInTree(AVLNode* node, int val) 
{
    if (node != nullptr) 
    {
        if (val &lt; node-&gt;val)
            return findNodeInTree(node-&gt;left, val);
        else if (val &gt; node-&gt;val) 
            return findNodeInTree(node-&gt;right, val);
        else 
            return node;
    }
    else 
        return nullptr;
}

int findMaxKeyInLef(AVLNode* node) 
{
    if (node == nullptr)
        return 0;
    else if (node-&gt;right == nullptr)
        return node-&gt;val;
    return findMaxKeyInLef(node-&gt;right);
}
AVLNode* delNodeFromTree(AVLNode** node, int val) 
{
    if (node == nullptr) 
        return nullptr;
    else if (val &lt; (*node)-&gt;val)
    {
        (*node)-&gt;left = delNodeFromTree(&amp;(*node)-&gt;left, val);
        //判断是否失衡,删了左子树一个结点,所以判断右子树高度是否过高
        if ((getHeight((*node)-&gt;right) - getHeight((*node)-&gt;left)) &gt; 1) 
            //右子树的左子树高度比右子树的右子树更高,相当于给右子树的右子树插入了新节点,相当于"右右"情况
            if (getHeight((*node)-&gt;right-&gt;left) &gt; getHeight((*node)-&gt;right-&gt;right))
                RL(node);
            else 
                RR(node);
        return (*node);
    }
    else if (val &gt; (*node)-&gt;val)
    {
        (*node)-&gt;right = delNodeFromTree(&amp;(*node)-&gt;right, val);
        //判断是否失衡,删了右子树一个结点,所以判断左子树高度是否过高
        if ((getHeight((*node)-&gt;left) - getHeight((*node)-&gt;right)) &gt; 1) 
            //左子树的左子树高度比右子树的右子树更高,相当于给左子树的左子树插入了新节点,相当于"左左"情况
            if (getHeight((*node)-&gt;left-&gt;left) &gt; getHeight((*node)-&gt;left-&gt;right))
                LL(node);
            else 
                LR(node);
        return (*node);
    }
    else if (val == (*node)-&gt;val)
    {
        //如果是叶子节点
        if ((*node)-&gt;left == nullptr &amp;&amp; (*node)-&gt;right == nullptr) 
        {
            delete (*node);
            (*node) = nullptr;
            return (*node);;
        }
        //如果左子树非空,将右子树续接到父节点
        else if ((*node)-&gt;left != nullptr) 
        {
            AVLNode* tmp = (*node)-&gt;left;
            delete (*node);
            return tmp;
        }
        //如果右子树非空,将左子树续接到父节点
        else if ((*node)-&gt;right != nullptr) 
        {
            AVLNode* tmp = (*node)-&gt;right;
            delete (*node);
            return tmp;
        }
        //左右子树皆非空
        else 
        {
            //寻找左子树中最大节点,即左子树中最右节点
            //(也可以寻找右子树中最小节点,即右子树中最左节点)
            int maxVal = findMaxKeyInLef((*node)-&gt;left);
            //交换这两个节点
            (*node)-&gt;val = maxVal;
            //删除那个用来交换的节点
            (*node)-&gt;left = delNodeFromTree(&amp;(*node)-&gt;left, maxVal);
            return *node;
        }
    }
}



AVLNode* root;

};

int main()
{
AVLTree tree;
vector<int> tmp = {99, 1, 34, 56, 23, 67, 78, 9, 45, 684, 35, 678, 234, 89, 90, 24, 672, 1, 1, 4};
for (auto x : tmp)
{
tree.insert(x);
}
tree.traverse();

AVLNode* p = tree.findNode(672);
if (p == nullptr)
    cout &lt;&lt; "672 is not in the tree" &lt;&lt; endl;
else 
    cout &lt;&lt; "succeed in finding " &lt;&lt; p-&gt;val &lt;&lt; endl;

tree.delNode(672);
tree.traverse();

p = tree.findNode(672);
if (p == nullptr)
    cout &lt;&lt; "672 is not in the tree" &lt;&lt; endl;
else 
    cout &lt;&lt; "succeed in finding " &lt;&lt; p-&gt;val &lt;&lt; endl;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275

测试结果

67 34 99 9 45 89 678 1 23 35 56 78 90 234 684 4 24 672
succeed in finding 672
67 34 99 9 45 89 678 1 23 35 56 78 90 234 684 4 24
672 is not in the tree

 
  
  
  • 1
  • 2
  • 3
  • 4

猜你喜欢

转载自blog.csdn.net/qq_39304851/article/details/115640746