平衡二叉樹(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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM