数据结构:关于AVL树的平衡旋转详解
前言
本文是基于你已经有一定的二叉排序树知识。如果你还是小白,可以参考我之前的博客:《数据结构:二叉搜索树(BST)的基本操作》。所以,在本文中不会再出现关于BST树的基本知识。
版权说明
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:Q-WHai
发表日期: 2015年12月28日
链接:https://qwhai.blog.csdn.net/article/details/50393548
来源:CSDN
更多内容:分类 >> 算法与数学
概述
AVL树又叫做平衡二叉树。前言部分我也有说到,AVL树的前提是二叉排序树(BST或叫做二叉搜索树)。由于在生成BST树的过程中可能会出现线型树结构,比如插入的顺序是:1, 2, 3, 4, 5, 6, 7..., n。在BST树中,比较理想的状况是每个子树的左子树和右子树的高度相等,此时搜索的时间复杂度是log(N)。可是,一旦这棵树演化成了线型树的时候,这个理想的情况就不存在了,此时搜索的时间复杂度是O(N),在数据量很大的情况下,我们并不愿意看到这样的结果。
现在我们要做的事就是让BST在创建的过程中不要向线型树发展。方法就是让其在添加新节点的时候,不断调整树的平衡状态。
定义:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
AVL树实现
1.类图
首先我们来看一下本文编写代码的类图(见图-1)。
因为考虑到图片内容的篇幅问题,类图中有一些地方的表述以eg.或getters(setters)省略掉了,详细内容请以代码为准。
知道策略模式或是看过我之前的博客《Java设计模式——策略模式》的都知道,这里我使用了策略模式来编写代码,4种不同的旋转方式明显的一个策略模式。这样一来,代码更加简捷,逻辑也更加清晰了。
图-1 AVL树程序类图
2.节点失衡
我们对于节点平衡有这样的定义:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。而这里提到的高度差,就是我们下面会引入的平衡因子:BF。
因为AVL树说到底还是一个二叉树,只有两个子节点。而且节点失衡的发生,是因为有一个新节点的插入,这个新插入的节点导致了某些节点左右子节点高度的不一致。所以我们可以枚举出以下4种情况的失衡状态。
(1)在一个节点的左子树的左子树上插入一个新节点。即LL。在这种情况下,我们可以通过将节点右旋使其平衡。如图-2所示;
图-2 LL单右旋操作
(2)在一个节点的右子树的右子树上插入一个新节点。即RR。在这种情况下,我们可以通过将节点左旋使其平衡。如图-3所示;
图-3 RR单左旋操作
(3)在一个节点的左子树的右子树上插入一个新节点。即LR。在这种情况下,我们不能直接通过将节点左旋或右来使其平衡了。这里需要两步来完成,先让树中高度较低的进行一次左旋,这个时候就变成了LL了。再进行一次单右旋操作即可。如图-4所示;
图-4 LR先左旋再右旋操作
(4)在一个节点的右子树的左子树上插入一个新节点。即RL。在这种情况下,我们不能直接通过将节点左旋或右来使其平衡了。这里需要两步来完成,先让树中高度较低的进行一次右旋,这个时候就变成了RR了。再进行一次单左旋操作即可。如图-5所示;
图-5 RL先右旋再左旋操作
3.旋转调节平衡
从上面对节点失衡的说明,以及图解。我想你已经对旋转的操作有了一个大概地认识了吧。从图中我们也可以看出,LL型和RR型、LR型和RL型是两个行为很相似地操作。其实他们互为对称。所以在下面的讲解中,我只针对LL型和LR型两种操作进行详细讲解,另外两种是类似的。
这里还有一点,可能读者会有一些不是太明白。就是我们在旋转的过程中,比如左旋,到底是怎么旋转法,以哪个节点为中心点旋转?
上面我们说到,失衡节点的平衡因子的绝对值是大于1的。AVL树失衡类型的判断都基于这个失衡节点的。也就是说LL型需要调整的是失衡节点的左子树的左子树,LR型需要调整的是失衡节点左子树的右子树。是不是有一点绕,看看上面的旋转操作图就应该知道了。那么,对于单旋转操作也就很简单了,就是以中间节点为中心旋转。而双旋转中,因为不是一次旋转,不能在一开始就确定旋转中心点,我们在第一次旋转的过程中,只是旋转后面的两个节点,并保证节点值的大小关系,如图-4所示。后面的问题就转化成了单旋转操作了。
LL型:
针对图-2所示的LL型失衡情况,可以看到节点15上是没有右孩子的,这个时候转成平衡之后,不用考虑节点15的右孩子与旋转平衡调节之后的节点20是否有冲突。
关键代码如下:
public class AdjustLL implements Adjustable {@Overridepublic void adjust(AVLTree tree, Node imbalanceNode) {Node parentNode = imbalanceNode.getParent();Node leftNode = imbalanceNode.getLeft(); // 新添加的节点resetImbalanceNode(imbalanceNode, leftNode);resetLeftNode(tree, leftNode, imbalanceNode, parentNode);resetParentNode(parentNode, leftNode);}/** 重置失衡节点* * @param imbalanceNode* 失衡节点*/private void resetImbalanceNode(Node imbalanceNode, Node leftNode) {... ...}/** 重置失衡节点的左节点* * @param leftNode* 左节点* @param imbalanceNode* 失衡节点* @param parentNode* 父节点*/private void resetLeftNode(AVLTree tree, Node leftNode, Node imbalanceNode, Node parentNode) {... ...}/** 重置父节点* * @param parentNode* 父节点* @param leftNode* 左节点*/private void resetParentNode(Node parentNode, Node leftNode) {... ...}
}
RR型:
类似LL型,详细代码请参见下面的GitHub源码。
RL型:
对于RL型的就比较麻烦一些了,比如我们现在插入的序列是这样的:{20, 15, 30, 40, 25, 23}。那么在调整平衡之前,我们的树结构是这样的(如图-6所示):
图-6 RL型失衡
这时我们就不能直接进行一次左旋或是右旋就可以搞定的了。虽然我们不能一步直接达到我们的要求,但是分两步就可以了呀。操作图示参见图-7.
图-7 RL型失衡的旋转过程
第一次右旋的关键代码:
/** 第一次右旋* (此处方法调用的顺序不要修改)* * @param tree* AVL树* @param imbalanceNode* 失衡节点* @param rightChildNode* 失衡节点的右孩子* @param rightLeftNode* 失衡节点右孩子的左孩子*/private void firstRightAdjust(AVLTree tree, Node imbalanceNode, Node rightChildNode, Node rightLeftNode) {firstRightChildAdjust(rightChildNode, rightLeftNode);firstRightLeftAdjust(imbalanceNode, rightChildNode, rightLeftNode);firstImbalanceAdjust(imbalanceNode, rightLeftNode);}/** 调整失衡节点* * @param imbalanceNode* 失衡节点* @param rightLeftNode* 失衡节点右孩子的左孩子*/private void firstImbalanceAdjust(Node imbalanceNode, Node rightLeftNode) {imbalanceNode.setRight(rightLeftNode);imbalanceNode.resetHeight();imbalanceNode.resetBF();}/** 调整失衡节点的右孩子* * @param rightChildNode* 失衡节点的右孩子* @param rightLeftNode* 失衡节点右孩子的左孩子*/private void firstRightChildAdjust(Node rightChildNode, Node rightLeftNode) {rightChildNode.setParent(rightLeftNode);rightChildNode.setLeft(null);rightChildNode.resetHeight();rightChildNode.resetBF();}/** 调整失衡节点右孩子的左孩子* * @param imbalanceNode* 失衡节点* @param rightChildNode* 失衡节点的右孩子* @param rightLeftNode* 失衡节点右孩子的左孩子*/private void firstRightLeftAdjust(Node imbalanceNode, Node rightChildNode, Node rightLeftNode) {rightLeftNode.setRight(rightChildNode);rightLeftNode.setParent(imbalanceNode);rightLeftNode.resetHeight();rightLeftNode.resetBF();}
第二次左旋的关键代码:
/** 第二次左旋* * @param tree* AVL树* @param imbalanceNode* 失衡节点* @param rightChildNode* 失衡节点的右孩子* @param rightLeftNode* 失衡节点右孩子的左孩子*/private void secondLeftAdjust(AVLTree tree, Node imbalanceNode, Node rightChildNode, Node rightLeftNode) {Node parentNode = imbalanceNode.getParent();secondRightChildAdjust(rightChildNode);secondImbalanceAdjust(imbalanceNode, rightLeftNode);secondRightLeftAdjust(tree, imbalanceNode, rightLeftNode, parentNode);}/** 调整失衡节点* * @param imbalanceNode* 失衡节点* @param rightLeftNode* 失衡节点右孩子的左孩子*/private void secondImbalanceAdjust(Node imbalanceNode, Node rightLeftNode) {if (rightLeftNode.getLeft() != null) {imbalanceNode.setRight(rightLeftNode.getLeft());}imbalanceNode.setParent(rightLeftNode);imbalanceNode.resetHeight();imbalanceNode.resetBF();}/** 调整失衡节点的右孩子* * @param rightChildNode* 失衡节点的右孩子*/private void secondRightChildAdjust(Node rightChildNode) {rightChildNode.resetHeight();rightChildNode.resetBF();}/** 调整失衡节点右孩子的左孩子* * @param tree* AVL树* @param imbalanceNode* 失衡节点* @param rightLeftNode* 失衡节点右孩子的左孩子* @param parentNode* 失衡节点的父节点*/private void secondRightLeftAdjust(AVLTree tree, Node imbalanceNode, Node rightLeftNode, Node parentNode) {rightLeftNode.setParent(parentNode);if (parentNode == null) {tree.resetRoot(rightLeftNode);}rightLeftNode.setLeft(imbalanceNode);rightLeftNode.resetHeight();rightLeftNode.resetBF();}
LR型:
类似RL型,详细代码请参见下面的GitHub源码。
Ref
- 《数据结构(C语言版)--清华大学出版社》
- 《大话数据结构--清华大学出版社》
GitHub源码下载
https://github.com/qwhai/simple-tree
数据结构:关于AVL树的平衡旋转详解相关推荐
- AVL树平衡旋转详解
AVL树平衡旋转详解 概述 AVL树又叫做平衡二叉树.前言部分我也有说到,AVL树的前提是二叉排序树(BST或叫做二叉查找树).由于在生成BST树的过程中可能会出现线型树结构,比如插入的顺序是:1, ...
- AVL树(二叉平衡树)详解与实现
公众号文章链接 AVL树概念 前面学习二叉查找树和二叉树的各种遍历,但是其查找效率不稳定(斜树),而二叉平衡树的用途更多.查找相比稳定很多.(欢迎关注数据结构专栏) AVL树是带有平衡条件的二叉查找树 ...
- 数据结构---AVL树调整方法(详)
AVL树的调整整体分为两种: 单旋转 左单旋 右单旋 双旋转 先左单旋,再右单旋 先右单旋,再左单旋 例如:上图想要将值为40的结点插入到AVL树中,只能将其插入到值为50的左孩子结点. 这里的平衡因 ...
- 【数据结构】AVL树(高度平衡的二叉搜索树)
AVL树(高度平衡的二叉搜索树) ①AVL树概念 ②平衡方法 <1>单旋 左单旋 右单旋 <2>双旋 先左后右旋 先右后左旋 ③代码实现 <1>所需头文件 < ...
- 数据结构-平衡二叉树(AVL树)
目录 1,平衡二叉树的介绍 1.1,二叉排序树存在的问题 1.2,平衡二叉树 1.3,平衡二叉树的创建 1.4,平衡二叉树的查找 2,代码实现 2.1,平衡二叉树的节点类型 2.2,LL旋转(单右旋转 ...
- [ 数据结构 - C++] AVL树原理及实现
问题引入: 在上文我们提到了二叉搜索树在按值顺序插入时,会形成单边树,会大大降低二叉搜索树的性能.因此我们要将二叉搜索树进行平衡,采取适当的平衡措施,从而减低二叉搜索树的高度,使二叉搜索树达到一个接近 ...
- 浅析AVL树--AVL树的双旋转
浅析AVL树–AVL树的双旋转 接上篇博文:浅析AVL树–AVL树的概念及单旋转 AVL树如何恢复平衡之双旋转 首先假设我们有一颗已经处于平衡的AVL树: 上篇博文已经解决了LL和RR两种情况的平衡恢 ...
- C++AVL树(自平衡二叉查找树)(附完整源码)
C++AVL树自平衡二叉查找树 node结构体定义 实现了以下几个接口 AVL树(自平衡二叉查找树)算法的完整源码(定义,实现,main函数测试) node结构体定义 typedef struct n ...
- c语言将AOE网络的数据写入TXT文档中,数据结构与算法学习辅导及习题详解.张乃孝版-C/C++文档类资源...
数据结构与算法学习辅导及习题详解.张乃孝版.04年10月 经过几年的努力,我深深体会到,编写这种辅导书要比编写一本湝通教材困难得多. 但愿我的上述理想,在本书中能够得以体现. 本书的组织 本书继承了& ...
最新文章
- Tianchi数据集最全更新!
- 跟安全技术大师学习黑客攻防技术 ——《黑客攻防技术宝典:web实战篇》
- 数据结构Java实现05----栈:顺序栈和链式堆栈
- Spark Worker启动源码
- 一台电脑怎么接两个显示器_电脑数码类目显示器 篇二:11.11抄作业,个人消费级显示器怎么选--20款好价显示器推荐_显示器...
- 计算机语言表示教师节快乐,表达教师节快乐的微信祝福语大汇总54句
- Linux 7 cmake:curses library not found
- HTML5 新元素标签系列:最简版 HTML5
- datax的工具配置oracle,完全小白级DataX安装配置过程详解
- 在CentOS下源码安装 Xen并搭建Windows虚拟机
- c# sha1签名 微信_C#微信公众号JS接口签名算法
- unity3d 求两个点长度_用Scratch3.0模拟求π的近似值(二) #寻找真知派#
- 数据加载中,请稍等......
- aws cloud map_Amazon EC2 – AWS Elastic Compute Cloud
- Vue中使用Video标签播放 <解析后的短视频>去水印视频无响应
- lwm2m协议 开源服务器,LwM2M协议接入
- 基于 Roslyn 实现代码动态编译
- Kubuntu 安装fcitx 5
- Playwright之初体验
- ehvierwer登录与不登录_【更新】亿寻—免登录不限速下载百度网盘
热门文章
- 初等数论--整除--线性组合与最大公因数之间的关系
- [计算机网络] 【谢希仁】考前突击复习二、三、四章
- 密码技术应用--RSA文件签名验签
- 密码技术--非对称加密算法及Go语言应用
- buu [GXYCTF2019]CheckIn
- MySQL—不相关子查询(多行子查询)
- python的openpyxl库如何读取特定列_通过渲染一百万个网页,来了解网络是如何崩溃的...
- BabyXor flower逆向寒假生涯(19/100)
- 从Github一开源项目ADSEC【学习域渗透攻防基础】
- 在Servlet中实现页面转发