前言

本文是基于你已经有一定的二叉排序树知识。如果你还是小白,可以参考我之前的博客:《数据结构:二叉搜索树(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树的平衡旋转详解相关推荐

  1. AVL树平衡旋转详解

    AVL树平衡旋转详解 概述 AVL树又叫做平衡二叉树.前言部分我也有说到,AVL树的前提是二叉排序树(BST或叫做二叉查找树).由于在生成BST树的过程中可能会出现线型树结构,比如插入的顺序是:1, ...

  2. AVL树(二叉平衡树)详解与实现

    公众号文章链接 AVL树概念 前面学习二叉查找树和二叉树的各种遍历,但是其查找效率不稳定(斜树),而二叉平衡树的用途更多.查找相比稳定很多.(欢迎关注数据结构专栏) AVL树是带有平衡条件的二叉查找树 ...

  3. 数据结构---AVL树调整方法(详)

    AVL树的调整整体分为两种: 单旋转 左单旋 右单旋 双旋转 先左单旋,再右单旋 先右单旋,再左单旋 例如:上图想要将值为40的结点插入到AVL树中,只能将其插入到值为50的左孩子结点. 这里的平衡因 ...

  4. 【数据结构】AVL树(高度平衡的二叉搜索树)

    AVL树(高度平衡的二叉搜索树) ①AVL树概念 ②平衡方法 <1>单旋 左单旋 右单旋 <2>双旋 先左后右旋 先右后左旋 ③代码实现 <1>所需头文件 < ...

  5. 数据结构-平衡二叉树(AVL树)

    目录 1,平衡二叉树的介绍 1.1,二叉排序树存在的问题 1.2,平衡二叉树 1.3,平衡二叉树的创建 1.4,平衡二叉树的查找 2,代码实现 2.1,平衡二叉树的节点类型 2.2,LL旋转(单右旋转 ...

  6. [ 数据结构 - C++] AVL树原理及实现

    问题引入: 在上文我们提到了二叉搜索树在按值顺序插入时,会形成单边树,会大大降低二叉搜索树的性能.因此我们要将二叉搜索树进行平衡,采取适当的平衡措施,从而减低二叉搜索树的高度,使二叉搜索树达到一个接近 ...

  7. 浅析AVL树--AVL树的双旋转

    浅析AVL树–AVL树的双旋转 接上篇博文:浅析AVL树–AVL树的概念及单旋转 AVL树如何恢复平衡之双旋转 首先假设我们有一颗已经处于平衡的AVL树: 上篇博文已经解决了LL和RR两种情况的平衡恢 ...

  8. C++AVL树(自平衡二叉查找树)(附完整源码)

    C++AVL树自平衡二叉查找树 node结构体定义 实现了以下几个接口 AVL树(自平衡二叉查找树)算法的完整源码(定义,实现,main函数测试) node结构体定义 typedef struct n ...

  9. c语言将AOE网络的数据写入TXT文档中,数据结构与算法学习辅导及习题详解.张乃孝版-C/C++文档类资源...

    数据结构与算法学习辅导及习题详解.张乃孝版.04年10月 经过几年的努力,我深深体会到,编写这种辅导书要比编写一本湝通教材困难得多. 但愿我的上述理想,在本书中能够得以体现. 本书的组织 本书继承了& ...

最新文章

  1. Tianchi数据集最全更新!
  2. 跟安全技术大师学习黑客攻防技术 ——《黑客攻防技术宝典:web实战篇》
  3. 数据结构Java实现05----栈:顺序栈和链式堆栈
  4. Spark Worker启动源码
  5. 一台电脑怎么接两个显示器_电脑数码类目显示器 篇二:11.11抄作业,个人消费级显示器怎么选--20款好价显示器推荐_显示器...
  6. 计算机语言表示教师节快乐,表达教师节快乐的微信祝福语大汇总54句
  7. Linux 7 cmake:curses library not found
  8. HTML5 新元素标签系列:最简版 HTML5
  9. datax的工具配置oracle,完全小白级DataX安装配置过程详解
  10. 在CentOS下源码安装 Xen并搭建Windows虚拟机
  11. c# sha1签名 微信_C#微信公众号JS接口签名算法
  12. unity3d 求两个点长度_用Scratch3.0模拟求π的近似值(二) #寻找真知派#
  13. 数据加载中,请稍等......
  14. aws cloud map_Amazon EC2 – AWS Elastic Compute Cloud
  15. Vue中使用Video标签播放 <解析后的短视频>去水印视频无响应
  16. lwm2m协议 开源服务器,LwM2M协议接入
  17. 基于 Roslyn 实现代码动态编译
  18. Kubuntu 安装fcitx 5
  19. Playwright之初体验
  20. ehvierwer登录与不登录_【更新】亿寻—免登录不限速下载百度网盘

热门文章

  1. 初等数论--整除--线性组合与最大公因数之间的关系
  2. [计算机网络] 【谢希仁】考前突击复习二、三、四章
  3. 密码技术应用--RSA文件签名验签
  4. 密码技术--非对称加密算法及Go语言应用
  5. buu [GXYCTF2019]CheckIn
  6. MySQL—不相关子查询(多行子查询)
  7. python的openpyxl库如何读取特定列_通过渲染一百万个网页,来了解网络是如何崩溃的...
  8. BabyXor flower逆向寒假生涯(19/100)
  9. 从Github一开源项目ADSEC【学习域渗透攻防基础】
  10. 在Servlet中实现页面转发