前言


  通过之前对二叉查找树的讨论,我们知道在给定节点数目的情况下,二叉树的高度越低,查找所用时间也就越短.

  在讨论红黑树的时候,我们说过红黑树并非完全"平衡"的二叉树,只是近似"平衡".那么这个平衡到底指的是什么呢?有没有完全"平衡"的二叉树?

平衡二叉树


  什么样的二叉树能被形容为平衡二叉树呢?

    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树的更多相关文章

  1. 树·AVL树/平衡二叉树

    1.AVL树 带有平衡条件的二叉查找树,所以它必须满足条件: 1 是一棵二叉查找树 2 满足平衡条件 1.1 平衡条件: 1)严格的平衡条件:每个节点都必须有相同高度的左子树和右子树(过于严格而不被使 ...

  2. 04-树4. Root of AVL Tree-平衡查找树AVL树的实现

    对于一棵普通的二叉查找树而言,在进行多次的插入或删除后,容易让树失去平衡,导致树的深度不是O(logN),而接近O(N),这样将大大减少对树的查找效率.一种解决办法就是要有一个称为平衡的附加的结构条件 ...

  3. 红黑树/B+树/AVL树

    RB Tree 红黑树  :http://blog.csdn.net/very_2/article/details/5722682 Nginx的RBTree实现   :http://blog.csdn ...

  4. 数据结构--树--AVL树

    详情查看:http://www.cnblogs.com/skywang12345/p/3577360.html

  5. 纸上谈兵:AVL树

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 二叉搜索树的深度与搜索效率 我们在树, 二叉树, 二叉搜索树中提到,一个有n个节点 ...

  6. 平衡搜索树(一) AVL树

    AVL树 AVL树又称为高度平衡的二叉搜索树,是1962年有俄罗斯的数学家G.M.Adel'son-Vel'skii和E.M.Landis提出来的.它能保持二叉树的高度 平衡,尽量降低二叉树的高度,减 ...

  7. 红黑树和AVL树的实现与比较-----算法导论

    一.问题描述 实现3种树中的两种:红黑树,AVL树,Treap树 二.算法原理 (1)红黑树 红黑树是一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是red或black.红黑树满足以 ...

  8. 二叉树,AVL树和红黑树

    为了接下来能更好的学习TreeMap和TreeSet,讲解一下二叉树,AVL树和红黑树. 1. 二叉查找树 2. AVL树 2.1. 树旋转 2.1.1. 左旋和右旋 2.1.2. 左左,右右,左右, ...

  9. 纸上谈兵: AVL树[转]

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 二叉搜索树的深度与搜索效率 我们在树, 二叉树, 二叉搜索树中提到,一个有n个节点 ...

随机推荐

  1. hibou 主界面自己侧滑的定义

    要打滑View参加UIPanGestureRecognizer #pragma mark 手势识别器回调方法 - (void)dragView:(UIPanGestureRecognizer *)ge ...

  2. Windows 应用程序结构

    Windows 应用程序结构

  3. 《JavaScript 闯关记》之简介

    简介 JavaScript 是面向 Web 的编程语言,绝大多数现代网站都使用了 JavaScript,并且所有的现代 Web 浏览器(电脑,手机,平板)均包含了 JavaScript 解释器. 这使 ...

  4. 不用css样式表和背景图片实现圆角矩形,超简洁!

    当网站页面的整体布局设计好后,接下来有很多细节的实现是很让人头疼的.其中之一就是圆角矩形的实现. 在网上看了很多圆角矩形的实现方法,基本有两种,一种是用纯css实现,不需要背景图片:另一种是用背景图像 ...

  5. 献给写作者的 Markdown 新手指南

    「简书」作为一款「写作软件」在诞生之初就支持了 Markdown,Markdown 是一种「电子邮件」风格的「标记语言」,我们强烈推荐所有写作者学习和掌握该语言.为什么?可以参考: 『为什么作家应该用 ...

  6. SSH框架中一些技巧、处理办法

    1.使用jstree插件时,操作成功直接刷新jstree 该页面(index.jsp)本身使用iframe框架jstree在leftFrame,操作页(add_input.jsp.add_succes ...

  7. Redis系列整理

    0.Redis系列-安装部署维护篇 1.Redis系列-远程连接redis并给redis加锁 2.Redis系列-存储篇string主要操作函数小结 3.Redis系列-存储篇list主要操作函数小结 ...

  8. UIButton 头文件常见属性和方法

    UIButton头文件常见属性 1.属性 contentEdgeInsets: default is UIEdgeInsetsZero.设置内容四边距,默认边距为0 @property(nonatom ...

  9. Git 和 SVN之间的五个基本区别

    GIT不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等.如果你是一个具有使用SVN背景的人,你需要做一定的思想转换,来适应GIT提供的一些概念和特征.所以,这篇文章的主要目的就是 ...

  10. JQuery给元素绑定click事件多次执行的解决方法

    原绑定方法: $(".subNavdiv").click(function () { ###### }); 这种方法只会在原click方法中继续添加新方法: 解决办法更改绑定方法为 ...