树 -- AVL树
前言
通过之前对二叉查找树的讨论,我们知道在给定节点数目的情况下,二叉树的高度越低,查找所用时间也就越短.
在讨论红黑树的时候,我们说过红黑树并非完全"平衡"的二叉树,只是近似"平衡".那么这个平衡到底指的是什么呢?有没有完全"平衡"的二叉树?
平衡二叉树
什么样的二叉树能被形容为平衡二叉树呢?
1)空树
2)根的左右子树的高度之差的绝对值不超过1,并且左右子树也都是一棵平衡二叉树。
这样的树就被我们认作时平衡二叉树.
显然,当一颗二叉搜索树能够保证上面的性质时,树整体的高度就能得到良好的控制.
二叉树失衡
既然我们已经知道了平衡二叉树的定义,并且了解到了它的诸多好处.那么如何将一颗二叉搜索树构建成平衡二叉搜索树呢? (我们首先约定,叶子节点的高度为1.NULL节点的高度为0.)
首先让我们分析一下插入和删除操作中,二叉搜索树"失衡"后的状态.如下图所示:

我们在2的左边插入了新的节点3后,使得对于1来讲,其左子树高度为2,右子树高度为1.进入了失衡的状态.
概括的讲,如果将平衡二叉树上某个节点的较高子树(比另一颗子树的高度大1)再增加1的高度,或者将某个节点的较低子树(比另一颗子树的高度小1)再减小1的高度,就会产生二叉树的失衡.
下面列举一下失衡的状态.
--泛型>
因为是1左侧的左侧的节点造成的失衡,所以称为LL失衡.
--泛型>
与LL失衡对称的RR失衡.
--泛型>
因为是1左侧的右侧的节点造成的失衡,所以成为LR失衡.
--泛型>
与LR失衡对称的RL失衡.
失衡二叉树的恢复操作
在红黑树中,我们对于失衡的调整是根据节点的颜色,进行旋转和赋值操作来保证一种"近似的平衡".
那么对于一颗要保证"平衡的"的二叉树,该如何调整呢?我们分情况讨论一下.
1)LL失衡与LR失衡
对于某处的LL失衡,需要进行右旋操作,化解此处的失衡.然后向上查看是否还存在其他类型的失衡.

对于RR失衡的修正类似于LL失衡的处理,旋转的方向改变成了左旋.
template<class T>
TNode<T>* AvlTree<T>::__leftleftRotate(TNode<T> *dest){
assert(dest!=nil || dest->left!=nil); TNode<T> *l = dest->left;
dest->left = l->right;
l->right = dest; dest->height = __maxHeight(dest->left,dest->right)+;
l->height = __maxHeight(l->left,l->right)+; if(root == dest)
root = l;
return l;
}
template<class T>
TNode<T>* AvlTree<T>::__rightrightRotate(TNode<T> *dest){
assert(dest!= || dest->right!=); TNode<T> *r = dest->right;
dest->right = r->left;//Between dest and new left
r->left = dest; dest->height = __maxHeight(dest->left,dest->right)+;
r->height = __maxHeight(r->left,r->right)+; if(root == dest)
root = r;
return r;
}
2) 对于LR失衡和RL失衡
对于LR失衡,我们需要先对2节点进行左旋操作,然后对1节点进行右旋操作.这样可以将1节点下的不平衡消除.之后需要向上查看是否有其他不平衡节点.

对于RL失衡的处理,与LR类似.
template<class T>
TNode<T>* AvlTree<T>::__leftrightRotate(TNode<T> *dest){
__rightrightRotate(dest->left);
return __leftleftRotate(dest);
}
template<class T>
TNode<T>* AvlTree<T>::__rightleftRotate(TNode<T> *dest){
__leftleftRotate(dest->right);
return __rightrightRotate(dest);
}
插入
能破坏一课平衡二叉树的操作为插入新节点,和删除已有节点.我们结合前面讲的平衡恢复方法来实现平衡二叉树的插入操作.
插入某个节点之后,可能会破坏插入所经过的路径上的节点的平衡度.所以需要回溯路径上节点当前的平衡性.这种场合十分适合递归的方法.
template<class T>
TNode<T>* AvlTree<T>::__insert(T val,TNode<T> *curr){
if(curr==nil){
TNode<T> *in = new TNode<T>(val,nil,nil,);
assert(in);
if(nil == root)
root = in;
curr = in;
}else if(curr->val > val){
curr->left = __insert(val,curr->left);
if(std::abs(height(curr->left)-height(curr->right))>){
if(val < curr->left->val){
curr = __leftleftRotate(curr);
}else{
curr = __leftrightRotate(curr);
}
}
}else if(curr->val < val){
curr->right = __insert(val,curr->right);
if(std::abs(height(curr->left)-height(curr->right))>){
if(val > curr->right->val){
curr = __rightrightRotate(curr);
}else{
curr = __rightleftRotate(curr);
}
}
}else{
std::cout<<"Duplicate val !\n"<<std::endl;
assert();
}
curr->height = __maxHeight(curr->left,curr->right) + ;
return curr;
}
删除
产出某个节点后带来的失衡就类似于在另一支上插入了一个节点而带来的失衡.
依然需要回溯路径上的节点.
template<class T>
TNode<T>* AvlTree<T>::__erase(TNode<T> *dest,TNode<T> *currRoot){
assert(dest); if(currRoot->val > dest->val){
currRoot->left = __erase(dest,currRoot->left); if(std::abs(height(currRoot->left)-height(currRoot->right))>){
if(height(currRoot->right->right) > height(currRoot->right->left))
currRoot = __rightrightRotate(currRoot);
else
currRoot = __rightleftRotate(currRoot);
}
}else if(currRoot->val < dest->val){
currRoot->right = __erase(dest,currRoot->right);
if(std::abs(height(currRoot->left)-height(currRoot->right)>)){
if(height(currRoot->left->left) > height(currRoot->left->right))
currRoot = __leftleftRotate(currRoot);
else
currRoot = __leftrightRotate(currRoot);
}
}else{//currRoot->val == dest->val
TNode<T> *replace;
if(currRoot->left!=nil && currRoot->right!=nil){
if(height(currRoot->left) > height(currRoot->right)){
replace = predecessor(currRoot);
currRoot->val = replace->val;
currRoot->left = __erase(replace,currRoot->left);
}else{
replace = successor(currRoot);
currRoot->val = replace->val;
currRoot->right = __erase(replace,currRoot->right);
}
}else{
replace = (currRoot->left!=nil)?currRoot->left:currRoot->right;
delete dest;
currRoot = replace;
}
}
currRoot->height = ((currRoot==nil)?:__maxHeight(currRoot->left,currRoot->right)+);
return currRoot;
}
参考
http://www.cnblogs.com/skywang12345/p/3603935.html
树 -- AVL树的更多相关文章
- 树·AVL树/平衡二叉树
1.AVL树 带有平衡条件的二叉查找树,所以它必须满足条件: 1 是一棵二叉查找树 2 满足平衡条件 1.1 平衡条件: 1)严格的平衡条件:每个节点都必须有相同高度的左子树和右子树(过于严格而不被使 ...
- 04-树4. Root of AVL Tree-平衡查找树AVL树的实现
对于一棵普通的二叉查找树而言,在进行多次的插入或删除后,容易让树失去平衡,导致树的深度不是O(logN),而接近O(N),这样将大大减少对树的查找效率.一种解决办法就是要有一个称为平衡的附加的结构条件 ...
- 红黑树/B+树/AVL树
RB Tree 红黑树 :http://blog.csdn.net/very_2/article/details/5722682 Nginx的RBTree实现 :http://blog.csdn ...
- 数据结构--树--AVL树
详情查看:http://www.cnblogs.com/skywang12345/p/3577360.html
- 纸上谈兵:AVL树
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 二叉搜索树的深度与搜索效率 我们在树, 二叉树, 二叉搜索树中提到,一个有n个节点 ...
- 平衡搜索树(一) AVL树
AVL树 AVL树又称为高度平衡的二叉搜索树,是1962年有俄罗斯的数学家G.M.Adel'son-Vel'skii和E.M.Landis提出来的.它能保持二叉树的高度 平衡,尽量降低二叉树的高度,减 ...
- 红黑树和AVL树的实现与比较-----算法导论
一.问题描述 实现3种树中的两种:红黑树,AVL树,Treap树 二.算法原理 (1)红黑树 红黑树是一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是red或black.红黑树满足以 ...
- 二叉树,AVL树和红黑树
为了接下来能更好的学习TreeMap和TreeSet,讲解一下二叉树,AVL树和红黑树. 1. 二叉查找树 2. AVL树 2.1. 树旋转 2.1.1. 左旋和右旋 2.1.2. 左左,右右,左右, ...
- 纸上谈兵: AVL树[转]
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 二叉搜索树的深度与搜索效率 我们在树, 二叉树, 二叉搜索树中提到,一个有n个节点 ...
随机推荐
- Linux之旅(1): diff, patch和quilt (下)
Linux之旅(1): diff, patch和quilt (下) 2 quilt 我们自己的项目能够用cvs或svn管理所有代码.但有时我们要使用其它开发人员维护的项目.我们须要改动一些文件,但又不 ...
- Windows文件操作的API函数[转载]
在VC中,大多数情况对文件的操作都使用系统提供的 API 函数,但有的函数我们不是很熟悉,以下提供一些文件操作 API 函数介绍: 一般文件操作 API CreateFile 打开文件 要对文件进行读 ...
- 理解js异步的概念
js引擎在执行的时候是单线程的,这是大家都知道的.我们先来看一段代码: <html> <head> <meta http-equiv="Content-Type ...
- (五)认识Android中的Service
一.使用Service 1.右击java文件夹,选择新建Service,然后重写其中的onStartCommand函数,只要执行了startService函数,onStartCommand便会被执行 ...
- SQL Server 死锁检查
示例代码 select spid, blocked, status, hostname, program_name, hostprocess, cmd from sysprocesses -- kil ...
- 在windows平台下忘记了root的密码如何解决?
1.打开MySQL配置文件 my.ini中,添加上skip-grant-tables,可以添加到文件的末尾或者是这添加到[mysqld]的下面. 2.然后重启MYSQL服务 windows环境中: n ...
- [转]用Gson来解析Json数据
转自太阳尚远的博客:http://blog.yeqianfeng.me/2016/03/02/use_gson_to_parse_json/ 在我们实际开发中像Json和XML这两种格式的数据是最常见 ...
- [Python]从豆瓣电影批量获取看过这部电影的用户列表
前言 由于之后要做一个实验,需要用到大量豆瓣用户的电影数据,因此想到了从豆瓣电影的“看过这部电影 的豆瓣成员”页面上来获取较为活跃的豆瓣电影用户. 链接分析 这是看过"模仿游戏"的 ...
- 安卓linearlayout布局的一个嵌套实例
XML代码如下 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...
- MFC使用Windows media player播放声音文件
一.在需要播放声音的资源上添加控件 资源视图 . 选择添加控件的资源(如对话框).右键单击.插入ActiveX控件.调整你需要的控件属性并记录ID 二.在项目中添加播放声音的类 点击菜单中的项目.添 ...