AVL树旋转
什么是AVL树?
AVL树是带有平衡条件的二叉查找树,一颗AVL树首先是二叉查收树(每个节点如果有左子树或右子树,那么左子树中数据小于该节点数据,右子树数据大于该节点数据),其次,AVL树必须满足平衡条件:每个节点的左子树和右子树的高度最多相差1(空树的高度定义为-1)。
什么是旋转?AVL树为什么需要用到旋转?
由于AVL树本身的性质,当我们插入节点时,有可能会破坏AVL树的平衡性,使一棵树的左子树和右子树的高度相差大于1,此时就需要对树进行一些简单的修正来恢复其性质,这个修正的过程就叫做旋转。
我们来看一个简单的例子,比如这棵树,他在插入节点之后不满足AVL树的性质,这时我们可以使用一个旋转来使他成为一颗AVL树。
旋转前: 3
/
2
/
1
这棵树根节点为3,插入2之后左右子树高度相差1,再插入1之后左右子树高度相差2(左子树高度为1,右子树高度为-1),此时这棵树不满足AVL树的条件,对这棵树进行旋转操作。
旋转后: 2
/ \
1 3
在经过一次旋转之后,这棵树的根节点为2,左右子树分别为1和3,满足AVL树的条件,插入完成。
如何对结点进行旋转,使其满足AVL树的条件?
·单旋转:
当新插入的节点在二叉树的外侧(左子树的左侧或右子树的右侧),并且此时破坏了AVL树的平衡,我们使用一个单旋转来恢复AVL树的性质。
以左侧单旋转为例,比如刚才那个例子中,旋转前根节点为3,左子树高度为1,右子树高度为-1。此时我们先让左子树2的右子树(在这里为NULL)变为根节点的新左子树
3
2 / \
/ NULL NULL
1
再让原来的根节点3变为节点2的右子树
2
/ \
1 3
此时可以算是完成了一次单旋转,2变为新的根节点。这个旋转后的树满足AVL树的条件。
左侧单旋转代码:
typedef struct TreeNode
{
ElementType Element;
struct TreeNode *Left;
struct TreeNode *Right;
int Height;
}*AvlTree;
int NodeHeight(AvlTree P)
{
if(P == NULL) return -;
else return P->Height;
}
AvlTree SingleRotateWithLeft(AvlTree T)
{
/* T指向原来的根节点,T1指向旋转后的根节点 */
AvlTree T1;
T1 = T->Left; /* 根节点的左子树等于其原来左子树的右子树 */
T->Left = T1->Right; /* 让原来的根节点成为新的根节点的右子树 */
T1->Right = T; /* 重新设置节点高度 */
T->Height = Max(NodeHeight(T->Left),NodeHeight(T->Right))+;
T1->Height = Max(NodeHeight(T1->Left),T->Height)+; /* 将新的根节点返回 */
return T1;
}
右侧单旋转和左侧差不多:
1 1
\ / \
2 2 3
\
3
AvlTree SingleRotateWithRight(AvlTree T)
{
AvlTree T1;
T1 = T->Right;
T->Right = T1->Left;
T1->Left = T;
T->Height = Max(NodeHeight(T->left),NodeHeight(T->Right))+;
T1->Height = Max(T->Height,NodeHeight(T1->Right))+;
return T1;
}
·双旋转
当新插入的节点在二叉树的内侧(左子树的右侧或右子树的左侧),并且此时破坏了AVL树的平衡,我们使用一个单旋转来恢复AVL树的性质。
这里还是先以左侧双旋转为例,我们来尝试建立一棵树并初始化,并设根节点为3
3
/ \
NULL NULL
我们插入一个1,由于这个树应满足二叉查找树的条件,所以1应该插入根节点3的左侧
3
/ \
1 NULL
再插入一个2,由二叉查找树条件,2应该插在1的右侧
3
/ \
1 NULL
\
2
此时,由于根节点左子树和右子树高度相差大于一,所以此时不满足AVL树的条件,此时需要一个双旋转来使这棵树成为AVL树
首先,我们对根节点的左子树1进行右侧单旋转:
(根据单旋转的方法,令 1 的右子树等于原来右子树 2 的左子树 NULL ,再让 1 成为 2 的左子树,原来指向 1 的指针指向 2)
3
/
2
/
1
然后,再对根节点3进行左侧单旋转:
(根据单旋转的方法,令 3 的左子树等于原来左子树 2 的右子树 NULL ,再让 3 成为 2 的右子树,2成为根节点)
2
/ \
1 3
此时,完成了一个双旋转,这棵树满足AVL树的条件。
看代码:
AvlTree DoubleRotateWithLeft(AvlTree T)
{
// 在根节点的左子树进行右侧单旋转
T->Left = SingleRotateWithRight(T->Left); // 在根节点处进行左侧单旋转
return SingleRotateWithLeft(T);
}
在右侧进行双旋转和左侧类似:
1
\
3
/
2
对根节点的右子树进行左侧单旋转:
1
\
2
\
3
对根节点进行右侧单旋转:
2
/ \
1 3
AvlTree DoubleRotateWithRight(AvlTree T)
{
// 在T的右子树进行左侧单旋转
T->Right = SingleRotateWithLeft(T->Right); // 在根节点T处进行右侧单旋转
return SingleRotateWithRight(T);
}
至此,我们已经看到了AVL树的四种旋转(左右单旋转,左右双旋转),有了这些旋转的方法,我们就可以在插入节点时进行判断,判断当前插入节点之后的树是否需要进行旋转,以及需要哪种旋转,进而实现任意在AVL树中插入节点。
具体的插入节点代码实现不在这里放出,可以参考《数据结构与算法分析-C语言描述版》(本文中的观点与代码大都来自此书,稍有改动,加入自己的理解)。
看一下代码实现后的运行结果:

注: 这里输入的最后一个参数 -1000 是输入的结束条件,输出的树是逆时针旋转90°之后的树。
AVL树旋转的更多相关文章
- 我的新发现:AVL树旋转的一个特性
关于AVL树旋转的代码网络上铺天盖地. 一些经典的实现方法如下: AVLTree SingleLeftRotation(AVLTree A) { AVLTree B = A->left; A-& ...
- (精)AVL树旋转共8种情况(涵盖所有考研的范围)
- PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由
03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...
- AVL树总结
定义:一棵AVL树或者是空树,或者是具有下列性质的二叉搜索树:它的左子树和右子树都是AVL树,且左右子树的高度之差的绝对值不超过1 AVL树失衡旋转总结: 假如以T为根的子树失衡.定义平衡因子为 H( ...
- DS AVL树详解
先说说二叉搜索树: 是有序的二叉树,根值>左节点值,右节点值>根值. 如果要查找某个值,二叉搜索树和二分查找一样,每进行一次值比较,就会减少一半的遍历区间. 但是,如果树插入的值一直递增/ ...
- 数据结构-AVL树的旋转
http://blog.csdn.net/GabrieL1026/article/details/6311339 平衡二叉树在进行插入操作的时候可能出现不平衡的情况,AVL树即是一种自平衡的二叉树,它 ...
- AVL树的插入操作(旋转)图解
=================================================================== AVL树的概念 在说AVL树的概念之前,我们需要清楚 ...
- AVL树的旋转操作详解
[0]README 0.0) 本文部分idea 转自:http://blog.csdn.net/collonn/article/details/20128205 0.1) 本文仅针对性地分析AVL树的 ...
- AVL树的单双旋转操作
把必须重新平衡的节点称为å.对于二叉树,å的两棵子树的高度最多相差2,这种不平衡可能有四种情况: 对å的左儿子的左子树进行插入节点(左-左) 对å的左儿子的右子树进行插入节点(左-右) 对å的右儿子的 ...
随机推荐
- INS(Instagram)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?
1.打开Ins,找到双重验证界面 打开Ins,点击右上角“三”-“设置”-“安全”-“双重验证”-“选择安全验证方式”-“身份验证应用”-“立即开启”-“手动设置”-“复制密钥”-“输入验证码” ...
- python-多任务编程05-协程(coroutine)
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源). 为啥说它是一个执行单元,因为它自带CPU上下文.这样只要在合适的时机, 我们可以把一个协程 切换 ...
- mybatis sqlsession与sqlsquery、transaction、connection
sqlsession和connection 一个sqlsession一般对应一个connection,并且mybatis默认每次获取session都会开启一个事务,且不自动提交事务.如果更新操作完成后 ...
- 使用ASP.NET实现定时计划任务,不依靠windows服务
我们怎样才能在服务器上使用asp.net定时执行任务而不需要安装windows service?我们经常需要运行一些维护性的任务或者像发送提醒邮件给用户这样的定时任务.这些仅仅通过使用Windows ...
- nginx location proxy_pass 后面的url 加与不加/的区别
在nginx中配置proxy_pass时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走. 首先是l ...
- manual for emacs markdown-mode(English)
markdown-mode now requires Emacs 24.3 or later. Markup insertion and replacement keybindings under C ...
- Day01_搭建环境&CMS服务端开发
学成在线 第1天 讲义-项目概述 CMS接口开发 1 项目的功能构架 1.1 项目背景 受互联网+概念的催化,当今中国在线教育市场的发展可谓是百花齐放.如火如荼. 按照市场领域细分为:学前教育.K12 ...
- 自述:自学Java应该注意什么问题?
Hello,大家好,我是若风,我是一名IT从业者,纵观当今局势,国内IT互联网行业发展是比较好的,当然学IT技术的人员也特别多,网上的学习资源也非常多,现在有很多人在学技术,想想要进入到 IT这个行业 ...
- PHP cal_to_jd() 函数
------------恢复内容开始------------ 实例 把 2007 年 6 月 20 日(格利高里历法)转换为儒略日计数: <?php$d=cal_to_jd(CAL_GREGOR ...
- luogu P2525 Uim的情人节礼物 其之壱
LINK:Uim的情人节礼物·其之壱 壱 古代通壹 常在日文中出现. 完全可以使用STL -->prev_permutation来解决. 不过我简单了解了一下康托展开. 这是一个一个排列对应一个 ...