首页 > 代码库 > 平衡二叉树-AVL树的构建和操作

平衡二叉树-AVL树的构建和操作

首先上一下单纯的二叉树插入节点的代码,用的是不返回新根节点的方式:

 1 void insertNode(BTree *&root,int data) 2 { 3     if (root == nullptr)//当根节点为NULL时,在根节点上插入 4     { 5         root = initRoot(data); 6         return; 7     } 8     if (root->data =http://www.mamicode.com/= data)//当插入数据等于根节点时,不插入 9     {10         return;11     }12     if (root->data > data)//如果当前节点比插入节点大,那么插入在左子树进行13     {14         insertNode(root->left, data);15     }16     else//否则在右子树进行17     {18         insertNode(root->right, data);19     }20 }

上面提到的initRoot()函数如下:

1 BTree *initRoot(int data)2 {3     BTree *root = new BTree;4     root->data =http://www.mamicode.com/ data;5     root->left = nullptr;6     root->right = nullptr;7     return root;8 }

这里使用了C++的内存管理方式:new

为了了解如何释放申请的这些块,我写了这么一个删掉整个树的函数:

 1 void deleteTree(BTree *&root) 2 { 3     if (root == nullptr) 4         return; 5     if (root->left != nullptr) 6         deleteTree(root->left); 7     if (root->right != nullptr) 8         deleteTree(root->right); 9     delete root;10     root = nullptr;11 }

所有函数如下(为了不占太多地方,折叠了起来):

 1 #include "iostream" 2  3 typedef struct _avlTree_//结构类中的成员默认都是共有的(Public) 4 { 5     int data; 6     struct _avlTree_ *left; 7     struct _avlTree_ *right; 8 }BTree; 9 10 BTree *initRoot(int data)11 {12     BTree *root = new BTree;13     root->data =http://www.mamicode.com/ data;14     root->left = nullptr;15     root->right = nullptr;16     return root;17 }18 19 void insertNode(BTree *&root,int data)20 {21     if (root == nullptr)//当根节点为NULL时,在根节点上插入22     {23         root = initRoot(data);24         return;25     }26     if (root->data =http://www.mamicode.com/= data)//当插入数据等于根节点时,不插入27     {28         return;29     }30     if (root->data > data)//如果当前节点比插入节点大,那么插入在左子树进行31     {32         insertNode(root->left, data);33     }34     else//否则在右子树进行35     {36         insertNode(root->right, data);37     }38 }39 40 void deleteTree(BTree *&root)41 {42     if (root == nullptr)43         return;44     if (root->left != nullptr)45         deleteTree(root->left);46     if (root->right != nullptr)47         deleteTree(root->right);48     delete root;49     root = nullptr;50 }51 52 int main(void)53 {54     BTree *root = initRoot(5);55     insertNode(root, 8);56     insertNode(root, 2);57     insertNode(root, 1);58     insertNode(root, 4);59     insertNode(root, 3);60     insertNode(root, 7);61     insertNode(root, 9);62     deleteTree(root);63     system("pause");64     return 0;65 }
View Code

当然为了使用system("pause"),iostream是不可不加的(当然也是为了简单)。

 

为了进行平衡二叉树的构建,我们需要进行如下操作:

1.为树的节点添加深度标识;

2.通过搜索深度进行判断哪里出现不符合AVL标准的节点;

3.根据情况决定使用单旋转还是双旋转;

所有的这些操作除了需要建立独立函数之外,全部都可以集中到插入操作中,在插入完毕之后就可以进行判断,然后进行平衡操作。

 

从易到难,先来进行深度的添加:

二叉树结构体:

1 typedef struct _avlTree_//结构类中的成员默认都是共有的(Public)2 {3     int data;4     struct _avlTree_ *left;5     struct _avlTree_ *right;6     int height;//深度标识7 }BTree;

返回节点深度的函数:

1 int getHeight(BTree *root)2 {3     if (root == nullptr)4         return -1;5     else6         return root->height;7 }

最主要的------>插入的函数:

 1 void insertNode(BTree *&root,int data) 2 { 3     if (root == nullptr) 4         root = initRoot(data); 5     else if (root->data =http://www.mamicode.com/= data) 6         return; 7     else if (root->data > data) 8         insertNode(root->left, data); 9     else if (root->data < data)10         insertNode(root->right, data);11 12     root->height = Max(getHeight(root->left), getHeight(root->right)) + 1;13 }

返回最大值的函数:

1 int Max(const int &x,const int &y)2 {3     if (x > y)4         return x;5     else6         return y;7 }
View Code

为了更加方便的实现深度的标注和平衡与否的检测,这里使用的深度有如下特征(因为跟我想象中的不一样,所以记录一下):

1.根节点有最大深度;

2.任何一个叶子都是0深度;

3.深度从叶子到根递增;

4.某父节点的左右节点的深度不一定一样;

(我想象的深度是从根节点为0深度向下递增,相同水平线上的深度相同。)

 

 

 

为了能够全自动(区别于人工构建)构建平衡树,需要了解会产生深度差的情况,因此会发现可能的结果只有四种:

1.在根的左子的左子

2.在跟的右子的右子

3.在根左子的右子

4.在根右子的左子

分别对应如下图示:

在右侧插入的两图应当类似,为了省事就没画;

上述两幅图体现了插入后产生的不平衡情况和其解决方式,前图称为单旋转,后图称为双旋转;

 

我们就是在这个理论基础上实现在插入过程中进行判断并旋转:改进后的插入函数为(注意其他函数有变化,全部代码请到文章最后找):

 1 BTree *insertNode(BTree *&root,const int &data) 2 { 3     if (root == nullptr)//如果要被插入节点的树为空树,就初始化它并返回地址 4     { 5         root = new BTree; 6         root->left = nullptr; 7         root->right = nullptr; 8         root->data =http://www.mamicode.com/ data; 9     }10     else if (root->data > data)11     {12         //以下重点理解13         root->left = insertNode(root->left, data);14         if (getHeight(root) - getHeight(root->right) >= 2)15         {16             if (data < root->left->data)17                 root = singleRotateWithLeft(root);18             else19                 root = doubleRotateWithLeft(root);20         }21     }22     else if (root->data < data)23     {24         root->right = insertNode(root->right, data);25         if (getHeight(root) - getHeight(root->left) >= 2)26         {27             if (data>root->right->data)28                 root = singleRotateWithRight(root);29             else30                 root = doubleRotateWithRight(root);31         }32     }33 34     root->height = getMax(getHeight(root->left), getHeight(root->right)) + 1;35     return root;36 }

 

相应的四种旋转函数为:

 1 BTree *singleRotateWithLeft(BTree *&root)//以下分别是单旋转和双旋转的函数,每个都返回旋转之后的根节点 2 {//单左旋 3     BTree *k2 = root;//有深度差的被命名为k2 4     BTree *k1 = k2->left; 5  6     //旋转 7     k2->left = k1->right; 8     k1->right = k2; 9 10     return k1;11 }12 13 BTree *doubleRotateWithLeft(BTree *&root)14 {//双左旋15     BTree *k3 = root;16     BTree *k1 = k3->left;17     BTree *k2 = k1->right;18 19     //旋转20     k1->right = k2->left;21     k3->left = k2->right;22     k2->left = k1;23     k2->right = k3;24 25     return k2;26 }27 28 BTree *singleRotateWithRight(BTree *&root)29 {//单右旋30     BTree *k2 = root;31     BTree *k1 = k2->right;32 33     //旋转34     k2->right = k1->left;35     k1->left = k2;36 37     return k1;38 }39 40 BTree *doubleRotateWithRight(BTree *&root)41 {//单右旋42     BTree *k1 = root;43     BTree *k3 = k1->right;44     BTree *k2 = k3->left;45 46     //旋转47     k1->right = k2->left;48     k3->left = k2->right;49     k2->left = k1;50     k2->right = k3;51 52     return k2;53 }

 

全部代码如下:

  1 #include "iostream"  2   3 typedef struct _avlTree_//结构类中的成员默认都是共有的(Public)  4 {  5     int data;  6     struct _avlTree_ *left;  7     struct _avlTree_ *right;  8     int height;//深度标识  9 }BTree; 10  11 int getHeight(const BTree *root) 12 { 13     if (root == nullptr) 14         return -1; 15     else 16         return root->height; 17 } 18  19 int getMax(const int &x,const int &y) 20 { 21     if (x > y) 22         return x; 23     else 24         return y; 25 } 26  27 BTree *singleRotateWithLeft(BTree *&root)//以下分别是单旋转和双旋转的函数,每个都返回旋转之后的根节点 28 {//单左旋 29     BTree *k2 = root;//有深度差的被命名为k2 30     BTree *k1 = k2->left; 31  32     //旋转 33     k2->left = k1->right; 34     k1->right = k2; 35  36     return k1; 37 } 38  39 BTree *doubleRotateWithLeft(BTree *&root) 40 {//双左旋 41     BTree *k3 = root; 42     BTree *k1 = k3->left; 43     BTree *k2 = k1->right; 44  45     //旋转 46     k1->right = k2->left; 47     k3->left = k2->right; 48     k2->left = k1; 49     k2->right = k3; 50  51     return k2; 52 } 53  54 BTree *singleRotateWithRight(BTree *&root) 55 {//单右旋 56     BTree *k2 = root; 57     BTree *k1 = k2->right; 58  59     //旋转 60     k2->right = k1->left; 61     k1->left = k2; 62  63     return k1; 64 } 65  66 BTree *doubleRotateWithRight(BTree *&root) 67 {//单右旋 68     BTree *k1 = root; 69     BTree *k3 = k1->right; 70     BTree *k2 = k3->left; 71  72     //旋转 73     k1->right = k2->left; 74     k3->left = k2->right; 75     k2->left = k1; 76     k2->right = k3; 77  78     return k2; 79 } 80  81 BTree *insertNode(BTree *&root,const int &data) 82 { 83     if (root == nullptr)//如果要被插入节点的树为空树,就初始化它并返回地址 84     { 85         root = new BTree; 86         root->left = nullptr; 87         root->right = nullptr; 88         root->data =http://www.mamicode.com/ data; 89     } 90     else if (root->data > data) 91     { 92         //以下重点理解 93         root->left = insertNode(root->left, data); 94         if (getHeight(root) - getHeight(root->right) >= 2) 95         { 96             if (data < root->left->data) 97                 root = singleRotateWithLeft(root); 98             else 99                 root = doubleRotateWithLeft(root);100         }101     }102     else if (root->data < data)103     {104         root->right = insertNode(root->right, data);105         if (getHeight(root) - getHeight(root->left) >= 2)106         {107             if (data>root->right->data)108                 root = singleRotateWithRight(root);109             else110                 root = doubleRotateWithRight(root);111         }112     }113 114     root->height = getMax(getHeight(root->left), getHeight(root->right)) + 1;115     return root;116 }117 118 void deleteTree(BTree *&root)119 {120     if (root->left != nullptr)121         deleteTree(root->left);122     if (root->right != nullptr)123         deleteTree(root->right);124     if (root->left == nullptr && root->right == nullptr)125     {126         delete root;127         root = nullptr;128     }129 }130 131 int main(void)132 {133     BTree *root = nullptr;134     root = insertNode(root, 1);135     root = insertNode(root, 2);136     root = insertNode(root, 3);137     root = insertNode(root, 5);138     root = insertNode(root, 7);139     root = insertNode(root, 9);140     root = insertNode(root, 11);141     deleteTree(root);142     system("pause");143     return 0;144 }

 

原来是由于插入例程总想自己写所以总是出错,而且似乎书上面的代码跟我这个有一定的出入,譬如在判断高度差那里例程是用的根左子树和右子树的差,得到的结果也是与2比,但是我用根本身的高度和它左右比较却也没有问题,但是例程中的就可能有问题了。

按照书上的总出事故,因此耽搁了好久没做完,今天正好做一下,争取明天完成树部分的基本练习。

 

以上。

平衡二叉树-AVL树的构建和操作