平衡二叉树-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 == 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 }

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

 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 }

当然为了使用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 }

返回最大值的函数:

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

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

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 = 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比,但是我用根本身的高度和它左右比较却也没有问题,但是例程中的就可能有问题了。

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

以上。

时间: 2024-10-06 10:46:13

平衡二叉树-AVL树的构建和操作的相关文章

图解平衡二叉树,AVL树(一)

图解平衡二叉树,AVL树(一) 学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点. 在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前

java数据结构与算法之平衡二叉树(AVL树)的设计与实现

[版权申明]未经博主同意,不允许转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/53892797 出自[zejian的博客] 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) java数据结构与算法之栈(Stack)设

平衡二叉树,AVL树之图解篇

学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点.            在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前苏联的数学

算法学习笔记 平衡二叉树 AVL树

AVL树是最先发明的自平衡二叉查找树, 其增删查时间复杂度都是 O(logn), 是一种相当高效的数据结构.当面对需要频繁查找又经常增删这种情景时,AVL树就非常的适用.[ 博客地址:http://blog.csdn.net/thisinnocence ] AVL树定义 AVL树诞生于 1962 年,由 G.M. Adelson-Velsky 和 E.M. Landis 发明.AVL树首先是一种二叉查找树.二叉查找树是这么定义的,为空或具有以下性质: 若它的左子树不空,则左子树上所有的点的值均小

数据结构&amp;&amp;AVL树原理、插入操作详解及实现

1.基本概念 AVL树的复杂程度真是比二叉搜索树高了整整一个数量级--它的原理并不难弄懂,但要把它用代码实现出来还真的有点费脑筋.下面我们来看看: 2.AVL树是什么? AVL树本质上还是一棵二叉搜索树(因此读者可以看到我后面的代码是继承自二叉搜索树的),它的特点是: 1. 本身首先是一棵二叉搜索树. 2. 带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1. 例如: 5              5 / \            / \ 2   6          2  

图解:平衡二叉树,AVL树

学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点.            在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前苏联的数学

平衡二叉树,AVL树之代码篇

看完了第一篇博客,相信大家对于平衡二叉树的插入调整以及删除调整已经有了一定的了解,下面,我们开始介绍代码部分. 首先,再次提一下使用的结构定义 1 typedef char KeyType; //关键字 2 typedef struct MyRcdType //记录 3 { 4 KeyType key; 5 }RcdType,*RcdArr; 6 typedef enum MyBFStatus //为了方便平衡因子的赋值,这里进行枚举 7 { //RH,EH,LH分别表示右子树较高,左右子树等高

大话数据结构—平衡二叉树(AVL树)

平衡二叉树(Self-Balancing Binary Search Tree/Height-Balanced Binary Search Tree),是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1. 平衡二叉树的前提是二叉排序树,不是二叉排序树的都不是平衡二叉树. 平衡因子BF(Balance Factor):二叉树上节点的左子树深度减去右子树深度的值. 最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树. 下图中,新插入节点37时,距离它最近的平

AVL树学习(平衡二叉树)

一.基本概念 AVL树既是平衡二叉树.AVL树的定义首先要求该树是二叉查找树(满足排序规则),并在此基础上增加了每个节点的平衡因子的定义,一个节点的平衡因子是该节点的左子树树高减去右子树树高的值. =========================================================================== 1. 二叉查找树:又称二叉排序树/二叉搜索树,具有以下性质: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值: (2)若右子树不空,则