树 -- 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个节点 ...
随机推荐
- GDAL1.11版本号对SHP文件索引加速測试
GDAL库中对于矢量数据的读取中能够设置一些过滤器来对矢量图形进行筛选.对于Shapefile格式来说.假设数据量太大,设置这个过滤器时间慢的简直无法忍受.好在GDAL1.10版本号開始支持读取Sha ...
- Fault Diagnosability Infrastructure Overview
Fault Diagnosability Infrastructure Overview The fault diagnosability infrastructure aids in prevent ...
- 【稳定婚姻问题】【HDU1435】【Stable Match】
2015/7/1 19:48 题意:给一个带权二分图 求稳定匹配 稳定的意义是对于某2个匹配,比如,( a ---- 1) ,(b----2) , 如果 (a,2)<(a,1) 且(2,a)& ...
- ImageButton与Button
1.Button控件 Butotn控件,主要用来实现一些命令操作,通过注册监听事件来实现.首先需要在xml文档中放入一个button按钮. <Button android:id="@+ ...
- sql语句批量处理Batch
package Statement批量处理; import java.sql.Connection; import java.sql.DriverManager; import java.sql.St ...
- C# 利用file打印日志
public class FaceLog { public static void AppendInfoLog(string errMsg) { try { string Folder = Main. ...
- 音量强度转分贝db
//LPDIRECTSOUNDBUFFER如何设置声音大小?> //取值范围是0 ~ -10000, 0最大,-10000最小,单位是分贝 //0-100音量转换成分贝 double decib ...
- ES6新特性-----变量和字符串
一.变量 1. LET 我们都习惯用var 来声明变量,现在还可以用let来声明变量,两者的主要区别是作用域:var声明的变量作用域为包围它的函数,而let声明的变量作用域仅在它所在的块中.(在ES5 ...
- 算法分析-动态规划(cut_rod)
什么是动态规划,我们要如何描述它? 动态规划算法通常基于一个递推公式及一个或多个初始状态. 当前子问题的解将由上一次子问题的解推出.使用动态规划来解题只需要多项式时间复杂度, 因此它比回溯法.暴力法等 ...
- javascript instanceof
object instanceof constructor instanceof运算符用来检测constructor.prototype是否存在于参数object的原型链上. 对于instanceof ...