码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-12-16 23:53:45      阅读:425      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   ar   io   color   os   使用   sp   

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

 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 == 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 = 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 }

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

bubuko.com,布布扣
 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 = 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 == 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     else
6         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 == 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 }

返回最大值的函数:

bubuko.com,布布扣
1 int Max(const int &x,const int &y)
2 {
3     if (x > y)
4         return x;
5     else
6         return y;
7 }
View Code

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

1.根节点有最大深度;

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

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

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

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

 

 

 

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

1.在根的左子的左子

2.在跟的右子的右子

3.在根左子的右子

4.在根右子的左子

分别对应如下图示:

bubuko.com,布布扣

bubuko.com,布布扣

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

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

 

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

 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 = 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             else
19                 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             else
30                 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 = 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             else
110                 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树的构建和操作

标签:style   blog   http   ar   io   color   os   使用   sp   

原文地址:http://www.cnblogs.com/lhyz/p/4133928.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!