二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)
二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)
二叉查找树(BST)
特殊的二叉树,又称为排序二叉树、二叉搜索树、二叉排序树。
二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于或等于根结点的数据域,右子树上所有结点的数据域均大于根结点的数据域。如下图所示:

二叉查找树通常包含查找、插入、建树和删除操作。
二叉查找树的创建
对于一棵二叉查找树,其创建与二叉树的创建很类似,略有不同的是,二叉查找树,为了保证整棵树都关于根结点的大小呈左小右大的特征,在创建时,需要根据当前结点的大小来判断插入位置,给出如下代码:

template<typename T>
void BSTree<T>::createBSTreeByFile(ifstream &f){
T e;
queue<BSNode<T>*> q;
while(!f.eof()){
InputFromFile(f, e);
Insert(root, e);
}
}
template<typename T>
void BSTree<T>::Insert(BSNode<T>* &t, T x){//得用指针的引用,不然传参时由于形参实例化,并不能成功创建二叉树
if(t==NULL){
t = new BSNode<T>;
t->data = x;
t->lchild = t->rchild = NULL;
return;
}
if(x<=t->data){
Insert(t->lchild, x);
}
else{
Insert(t->rchild, x);
}
}

二叉查找树的查找
二叉查找树的查找有递归和非递归两种,对于递归方式,其递归边界为树的终止结点,非递归方式则采取对树中所有结点采取BFS或者DFS进行遍历的方式。
对于非递归方式,给出采取DFS的遍历方式,在这种方式中,通常采用入栈的方式,来访问每个结点,而根据访问的先后顺序,又分为,前序、中序和后序三种遍历方式。以前序遍历为例,通常以根、左、右的顺序访问遍历每个结点,而中序遍历方式,则以左、根、右的顺序遍历,后序则以左右根的顺序来访问。下面给出三种遍历方式的代码:前序遍历:

1 template<typename T>
2 void BSTree<T>::PreOrderTraverse(void(*visit)(BSNode<T>&))const{
3 stack<BSNode<T>*> s;
4 BSNode<T> *t = root;
5 while(NULL!=t || !s.empty()){
6 if(NULL!=t){
7 s.push(t);
8 visit(*t);
9 t = t->lchild;
10 }
11 else{
12 t = s.top();
13 s.pop();
14 t = t->rchild;
15 }
16 }
17 cout<<endl;
18 }

中序遍历:

1 template<typename T>
2 void BSTree<T>::InOrderTraverse(void(*visit)(BSNode<T>&))const{
3 stack<BSNode<T>*> s;
4 BSNode<T> *q;
5
6 q = root;
7
8 while(!s.empty()||q!=NULL){
9 if(q!=NULL){
10 s.push(q);
11 q = q->lchild;
12 }
13 else{
14 q = s.top();
15 s.pop();
16 visit(*q);
17 q = q->rchild;
18 }
19 }
20 cout<<endl;
21 }

后序遍历,对于后序遍历,直接采用入栈的方式进行访问,是不行的,因为根结点被访问两次,无法保证你在弹栈后,对该结点如何操作,因此,需要另设置一个flag参数,来指明该节点是否左右子树都访问过,代码如下,我这里是令定义一个结构体,来实现:

/*结构体部分*/
enum Tags{Left, Right};
template<typename T>struct StackElem
{
BSNode<T> *p;
Tags flag;
};
/*后序遍历代码部分*/
template<typename T>
void BSTree<T>::PostOrderTraverse(void(*visit)(BSNode<T>&))const{
StackElem<T> se;
stack<StackElem<T> > s;
BSNode<T> *t;
t = root;
if(t==NULL){
return;
}
while(t!=NULL||!s.empty()){
while(t!=NULL){
se.flag = Left;
se.p = t;
s.push(se);
t = t->lchild;
}
se = s.top();
s.pop();
t = se.p;
if(se.flag==Left){
se.flag = Right;
s.push(se);
t = t->rchild;
}
else{
visit(*t);
t = NULL;
}
}
}

以下是递归实现部分,递归实现,则是以二叉树边界为递归边界,前面已经说过了,其余逻辑与非递归一致,因为递归的过程,可以看作是一个入栈和弹栈的过程,即,在未到达边界时,通过递归,来访问下一个结点,例如左结点,当触及边界,则访问该结点,由于每次递归状态都被计算机保存,因此,在访问一个结点以后,返回上一个结点的状态,会依次访问上去。
递归前序遍历:

1 template<typename T>
2 void BSTree<T>::PreTraverse(BSNode<T> *t, void(*visit)(BSNode<T>&))const{
3 if(t==NULL){
4 return;
5 }
6 else{
7 visit(*t);
8 PreTraverse(t->lchild, visit);
9 PreTraverse(t->rchild, visit);
10 }
11 }

递归中序遍历:

1 template<typename T>
2 void BSTree<T>::InTraverse(BSNode<T> *t, void(*visit)(BSNode<T>&))const{
3 if(t==NULL){
4 return;
5 }
6 else{
7 InTraverse(t->lchild, visit);
8 visit(*t);
9 InTraverse(t->rchild, visit);
10 }
11 }

递归后序遍历:

1 template<typename T>
2 void BSTree<T>::PostTraverse(BSNode<T> *t, void(*visit)(BSNode<T>&))const{
3 if(t!=NULL){
4 PostTraverse(t->lchild, visit);
5 PostTraverse(t->rchild, visit);
6 visit(*t);
7 }
8 }

平衡二叉树(AVL树)
平衡二叉树是由前苏联的两位数学家G.M.Adelse-Velskil和E.M.Landis提出,因此一般也称作AVL树,AVL树本质还是一棵二叉查找树,只是在其基础上增加了“平衡”的要求。所谓平衡是指,对AVL树的任意结点来说,其左子树与右子树的高度之差的绝对值不超过1,其中左子树与右子树的高度因子之差称为平衡因子。
如下所示,就是一棵由{1,2,3,4,5,7,8}构建的AVL树:

只要能随时保证每个结点平衡因子的绝对值不超过1,AVL的高度就始终能保持O(logn)级别,由于需要对每个结点都得到平衡因子,因此需要在树的结构中加入一个变量height来记录以当前结点为根结点的子树的高度。
AVL树的创建
AVL树的创建是基于二叉查找树的插入代码的基础上,增加平衡操作的。需要从插入的结点开始从下往上判断结点是否失衡,因此,需要在调用insert函数以后,更新当前子树的高度,并在这之后根据树型来进行相应的平衡操作。那么,怎么进行平衡操作呢?AVL树的插入是需要采取左旋或者右旋操作的,即,插入后,由于插入操作,导致某棵子树的高度超过了另一棵子树高度的2个结点高度,这样就破坏了树的平衡性,需要做出调整。
右旋操作
如下所示一棵简单的AVL树,对其进行插入操作以后:

一棵简单的AVL树
变成了下图这样的AVL树:

这样子就失衡了,所谓右旋操作,就是将这棵AVL树,从最靠近插入结点的失衡结点处,通过往右子树调整,使整棵树的每个结点的平衡因子变为正常,不如上图的树,离插入节点3最近的失衡结点是7,

则可以通过下图所示的操作,来平衡二叉树,即调整整棵树平衡因子:

同样,左旋也与此类似。但是,如果5结点本身就有右结点,即如下所示:

这样,在经过右旋操作以后,这棵树还是不平衡的,旋转后这棵树如下所示:

因此,还需要进行一次旋转,显然,继续右旋已经无法满足我们的需求,那么要如何进行操作,才能使这棵树回复平衡呢?(在后续中,会进行讲解)
左旋操作
左旋操作与右旋操作是类似的,都属于对子树的单旋转。

左旋与右旋一样,同样也存在这样的问题,如果该树的右子树的左结点存在,则单一通过左旋是做不到的,那么应该如何处理呢?
其实,以L和R来表示,插入结点的位置,有以下四种情况:

从上表可以看出,左旋和右旋两种情况中,左右结点若存在的话,就是上表中的RL和LR情况。则,只需要对两种情况分别按照上表采取相应的操作就可以解决,如下图所示:
LR型

RL型

由此,就能实现AVL树的平衡,下面给出代码:
AVLTree.h

1 #ifndef _AVLTREE_H_
2 #define _AVLTREE_H_
3 #include "C.h"
4 #include "AVLNode.h"
5 #include "Function.h"
6
7 typedef int T;
8
9 using namespace std;
10
11 template<typename T>
12 class AVLTree{
13 private:
14 AVLNode<T> *root;
15 Destroy(AVLNode<T> *t){
16 if(t!=NULL){
17 Destroy(t->lchild);
18 Destroy(t->rchild);
19 delete t;
20 t = NULL;
21 }
22 return 0;
23 }
24 public:
25 AVLTree(){
26 root = NULL;
27 }
28 ~AVLTree(){
29 Destroy(root);
30 }
31
32 AVLNode<T>* newAVLNode(T x); //创建新结点
33 void Insert(AVLNode<T>* &t, T x);
34 void createAVLTreeFromFile(ifstream &f);
35 AVLNode<T>* Root()const;
36 int AVLTreeDepth(AVLNode<T> *t)const;
37 int getAVLTreeHeight(AVLNode<T>* t)const; //获取当前结点的高度
38 int getBalanceFactor(AVLNode<T>* t)const; //计算当前结点的高度
39 void updateAVLNodeHeight(AVLNode<T>* &t);
40 T getElem(AVLNode<T>* t)const;
41 bool getElemExist(AVLNode<T>* &t)const;
42 void LeftRotation(AVLNode<T>* &t);
43 void RightRotation(AVLNode<T>* &t);
44 void PreOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const;
45 void PostOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const;
46 };
47
48 template<typename T>
49 AVLNode<T>* AVLTree<T>::newAVLNode(T x){
50 AVLNode<T>* avlnode = new AVLNode<T>;
51 avlnode->data = x;
52 avlnode->height = 1;
53 avlnode->lchild = avlnode->rchild = NULL;
54 return avlnode;
55 }
56
57 template<typename T>
58 void AVLTree<T>::Insert(AVLNode<T>* &t, T x){
59 if(t==NULL){
60 t = newAVLNode(x);
61 return;
62 }
63 if(x==t->data){//结点已经存在,直接返回
64 return;
65 }
66 if(x < t->data){
67 Insert(t->lchild, x);
68 updateAVLNodeHeight(t);
69 if(getBalanceFactor(t)==2){
70 if(getBalanceFactor(t->lchild)==1){
71 RightRotation(t);
72 }
73 else if(getBalanceFactor(t->lchild)==-1){
74 LeftRotation(t->lchild);
75 RightRotation(t);
76 }
77 }
78 }
79 else{
80 Insert(t->rchild, x); //值比当前结点大,往右子树插入
81 updateAVLNodeHeight(t); //更新树高
82 if(getBalanceFactor(t)==-2){
83 if(getBalanceFactor(t->rchild)==-1){ //RR型
84 LeftRotation(t);
85 }
86 else if(getBalanceFactor(t->rchild)==1){
87 RightRotation(t->rchild);
88 LeftRotation(t);
89 }
90 }
91 }
92 }
93
94 template<typename T>
95 void AVLTree<T>::createAVLTreeFromFile(ifstream &f){
96 T e;
97 while(!f.eof()){
98 InputFromFile(f, e);
99 Insert(root, e);
100 }
101 }
102
103 template<typename T>
104 AVLNode<T>* AVLTree<T>::Root()const{
105 return root;
106 }
107
108 template<typename T>
109 int AVLTree<T>::AVLTreeDepth(AVLNode<T> *t)const{
110 int i,j;
111 if(t==NULL){
112 return 0;
113 }
114 else{
115 i = AVLTreeDepth(t->lchild);
116 j = AVLTreeDepth(t->rchild);
117 }
118 return i>j ? i+1 : j+1;
119 }
120
121 template<typename T>
122 int AVLTree<T>::getAVLTreeHeight(AVLNode<T>* t)const{
123 if(t==NULL){
124 return 0;
125 }
126 return t->height;
127 }
128
129 template<typename T>
130 int AVLTree<T>::getBalanceFactor(AVLNode<T>* t)const{
131 if(t==NULL){
132 return 0;
133 }
134 return getAVLTreeHeight(t->lchild) - getAVLTreeHeight(t->rchild);
135 }
136
137 template<typename T>
138 void AVLTree<T>::updateAVLNodeHeight(AVLNode<T>* &t){
139 t->height = max(getAVLTreeHeight(t->lchild), getAVLTreeHeight(t->rchild)) + 1;
140 }
141
142 template<typename T>
143 T AVLTree<T>::getElem(AVLNode<T>* t)const{
144 return t->data;
145 }
146
147 template<typename T>
148 bool AVLTree<T>::getElemExist(AVLNode<T>* &t)const{//判断当前结点是否为空
149 if(t!=NULL){
150 return true;
151 }
152 return false;
153 }
154
155 template<typename T>
156 void AVLTree<T>::LeftRotation(AVLNode<T>* &t){
157 AVLNode<T> *temp = t->rchild;
158 t->rchild = temp->lchild;
159 temp->lchild = t;
160 updateAVLNodeHeight(t);
161 updateAVLNodeHeight(temp);
162 t = temp;
163 }
164
165 template<typename T>
166 void AVLTree<T>::RightRotation(AVLNode<T>* &t){
167 AVLNode<T> *temp = t->lchild;
168 t->lchild = temp->rchild;
169 temp->rchild = t;
170 updateAVLNodeHeight(t);
171 updateAVLNodeHeight(temp);
172 t = temp;
173 }
174
175 template<typename T>
176 void AVLTree<T>::PreOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const{
177 if(t!=NULL){
178 visit(*t);
179 PreOrderTraverse(t->lchild, visit);
180 PreOrderTraverse(t->rchild, visit);
181 }
182 }
183
184 template<typename T>
185 void AVLTree<T>::PostOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const{
186 if(t!=NULL){
187 PostOrderTraverse(t->lchild, visit);
188 PostOrderTraverse(t->rchild, visit);
189 visit(*t);
190 }
191 }
192 #endif // _AVLTREE_H_

Function.h

1 #ifndef _FUNCTION_H_
2 #define _FUNCTION_H_
3 #include "C.h"
4 #include "AVLNode.h"
5 #include "AVLTree.h"
6
7 typedef int T;
8
9 using namespace std;
10
11 bool InputFromFile(ifstream &f, T &e){
12 f>>e;
13 return f.good();
14 }
15
16 void visit(AVLNode<T> &t){
17 cout<<t.data<<" ";
18 }
19
20 #endif // _FUNCTION_H_

C.h

1 #ifndef _C_H_ 2 #define _C_H_ 3 #include<iostream> 4 #include<string> 5 #include<stdio.h> 6 #include<algorithm> 7 #include<map> 8 #include<math.h> 9 #include<queue> 10 #include<stack> 11 #include<vector> 12 #include<fstream> 13 #include<assert.h> 14 #endif // _C_H_

AVLNode.h

1 #ifndef _AVLNODE_H_
2 #define _AVLNODE_H_
3
4 typedef int T;
5
6 template<typename T>
7 struct AVLNode{
8 int height; //平衡因子
9 T data; //数据域
10 AVLNode<T> *lchild, *rchild; //指针域
11 };
12 #endif // _AVLNODE_H_

二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)的更多相关文章
- 二叉查找树(BST)、平衡二叉树(AVL树)
二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于或等于根结点的数据域,右 ...
- 单例模式,堆,BST,AVL树,红黑树
单例模式 第一种(懒汉,线程不安全): public class Singleton { private static Singleton instance; private Singleton () ...
- AVL树的插入和删除
一.AVL 树 在计算机科学中,AVL树是最早被发明的自平衡二叉查找树.在AVL树中,任一节点对应的两棵子树的最大高度差为 1,因此它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度 ...
- 平衡二叉树,AVL树之图解篇
学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...
- 图解:平衡二叉树,AVL树
学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...
- Java 树结构实际应用 四(平衡二叉树/AVL树)
平衡二叉树(AVL 树) 1 看一个案例(说明二叉排序树可能的问题) 给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在. 左边 BST 存在的问题分析: ...
- AVL树的插入操作(旋转)图解
=================================================================== AVL树的概念 在说AVL树的概念之前,我们需要清楚 ...
- AVL 树的插入、删除、旋转归纳
参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339 1126号注:先前有一个概念搞混了: 节点的深度 Depth 是指从根 ...
- 二叉查找树、平衡二叉树(AVL)、B+树、联合索引
1. [定义] 二叉排序树(二拆查找树)中,左子树都比节点小,右子树都比节点大,递归定义. [性能] 二叉排序树的性能取决于二叉树的层数 最好的情况是 O(logn),存在于完全二叉排序树情况下,其访 ...
随机推荐
- HttpWebRequest using Basic authentication
System.Net.CredentialCache credentialCache = new System.Net.CredentialCache(); credentialCache.Add( ...
- iOS后台运行播放无声音频 测试可行
如果打回来了,就自认倒霉吧 制作无声音频. @interface AppDelegate () { NSInteger count; } @property(strong, nonatomic)NST ...
- C#常忘语法笔记(C#程序设计基础1-4章)
1.1 const:声明一个常量 1.2强转: double->int eg1: int i=(int)3.0; eg2: double d=3.0; int i=(int)d+1; strin ...
- java excel Workbook API
此文摘自:http://blog.sina.com.cn/zenyunhai 1. int getNumberOfSheets() 获得工作薄(Workbook)中工作表(Sheet)的个数,示例: ...
- Codeforces Round #540 Div. 3 F2
考虑将每种颜色构成的极小连通块缩点,然后直接跑树形dp即可,即f[i][0/1]表示子树内是否有颜色向上延伸时删边的方案数.dp时需要去除某点的贡献,最好用前后缀积的做法而不是求逆. 至于如何缩点,假 ...
- Girls and Boys HDU - 1068 二分图匹配(匈牙利)+最大独立集证明
最大独立集证明参考:https://blog.csdn.net/qq_34564984/article/details/52778763 最大独立集证明: 上图,我们用两个红色的点覆盖了所有边.我们证 ...
- Educational Codeforces Round 53 (Rated for Div. 2) D. Berland Fair
题意:一个人 有T块钱 有一圈商店 分别出售 不同价格的东西 每次经过商店只能买一个 并且如果钱够就必须买 这个人一定是从1号店开始的!(比赛的时候读错了题,以为随意起点...)问可以买多少个 ...
- Django+Xadmin打造在线教育系统(七)
全局导航&个人中心&全局搜索 配置全局导航 让index页面也继承base页面,注意首页有个单独的__index.js__ base页面的导航栏也进行配置 <nav> &l ...
- 搭建Google镜像网站
很多人FQ或者买VPN账号仅仅只是为了使用Google搜索.相对于搭建VPN服务器来说,下面的方法搭建Google镜像网站将更加便捷. 条件:最好有自己的域名(可选),有可以正常访问Google的服务 ...
- Centos 5 无法使用ifconfig命令
问题原因,在环境变量里没有包含文件夹 / sbin , 该文件夹下存有 ifconfig, 可以在终端下 cat /etc/profile, 可以发现没有关于 / sbin 的环境变量 解决方法:vi ...