AVL树的JAVA实现及AVL树的旋转算法
1,AVL树又称平衡二叉树,它首先是一颗二叉查找树,但在二叉查找树中,某个结点的左右子树高度之差的绝对值可能会超过1,称之为不平衡。而在平衡二叉树中,任何结点的左右子树高度之差的绝对值会小于等于 1。
2,为什么需要AVL树呢?在二叉查找树中最坏情况下查找某个元素的时间复杂度为O(n),而AVL树能保证查找操作的时间复杂度总为O(logn)。
对于一棵BST树而言,不仅有查找操作,也有插入、删除等改变树的形态的操作。随着不断地插入、删除,BST树有可能会退化成链表的形式,使得查找的时间复杂度变成O(N),这种情形下,BST树的结构非常不平衡了。为了保持树的平衡,需要对树的形态做一些限制,因此,引入了AVL树,以保证树的左右子树高度之差的绝对值小于等于1。
3,AVL树的JAVA代码实现:
AVLTree 继承 BinarySearchTree 并改写 添加节点的add方法,在add方法中判断插入元素后是否导致树不平衡,当不平衡时需要通过旋转进行调整。何时进行旋转调整是通过左右子树的高度之差进行判断的。这里通过rebalance方法使得某结点保持平衡:整个程序的完成代码参考最后。
private BinaryNodeInterface<T> rebalance(BinaryNodeInterface<T> nodeN){
int heightDifference = getHeightDifference(nodeN);
if(heightDifference > 1){//左子树比右子树高
if(getHeightDifference(nodeN.getLeftChild()) > 0)
nodeN = rotateRight(nodeN);//插入在左子树的左孩子中
else
nodeN = rotateLeftRight(nodeN);
}
else if(heightDifference < -1){//右子树比左子树高
if(getHeightDifference(nodeN.getRightChild()) < 0)
nodeN = rotateLeft(nodeN);//插入在右子树的右孩子中
else
nodeN = rotateRightLeft(nodeN);
}
return nodeN;
}
4,AVL树的平衡保证算法
当向AVL树中插入或者删除元素时,可能打破树的平衡。这时,需要通过旋转来调整使之重新变成一颗AVL树。(这里讨论插入元素时的调整)
设 N 表示最接近新叶子的不平衡结点,由于插入元素之前树是平衡的,则插入之后不会有比 N 更高的不平衡结点。(树根的高度为 1 )
一共有 4 种原因导致结点 N 不平衡:
①在 结点 N 的左孩子的左子树中发生了插入操作
进行右旋转调整:即对结点 N 进行右旋转,算法如下:
算法 rotateRight(nodeN)
nodeL = nodeN 的左孩子
将nodeN 的左孩子置为 nodeL 的右孩子
将 nodeL 的右孩子置为 nodeN
return nodeL
②在 结点N 的右孩子的右子树中发生了插入操作
进行左旋转调整:即对结点N进行左旋转,算法如下:
算法 rotateLeft(nodeN)
nodeR = nodeN 的右孩子
将 nodeN 的右孩子置为 nodeR 的左孩子
将 nodeR 的左孩子置为 nodeN
return nodeR
③在 结点N 的右孩子的左子树中发生了插入操作
进行右-左旋转,即先对结点N 的孙子(孩子的孩子,"位于新插入元素的那个方向")进行右旋转;再对结点N 的新孩子进行左旋转,算法如下:
算法 rotateRightLeft(nodeN)
nodeR = nodeN 的右孩子
将 nodeN 的右孩子置为由 rotateRight(nodeN 的孩子的孩子)返回的结点
return rotateLeft(nodeN 的新的右孩子)
④在 结点N 的左孩子的右子树中发生了插入操作
进行左-右旋转,即先对结点N的孙子进行左旋转,再对结点N的新孩子进行右旋转,算法如下:
算法 rotateLeftRight(nodeN)
nodeL = nodeN 的左孩子
将 nodeN 的左孩子置为由 rotateLeft(nodeN 孩子的孩子)返回的结点
return rotateLeft(nodeN 新的左孩子)
5,关于树的平衡性的进一步讨论
除了AVL树之外,还有2-3树、2-4树、红黑树等一系列带有平衡性质的树。其中2-3树和2-4树是完全平衡的,即所有的叶子位于同一层。2-3树的结点至多有两个数据元素,2-4树的结点至多有三个数据元素,这样使得在相同的结点数目下,一般,AVL树比2-3树要高,2-3树比2-4树要高,但是在查找过程中内部结点的比较次数2-4树比2-3要多,2-3树比AVL树要多。因此,总体上看,AVL树、2-3树、2-4树、红黑树的查找都是O(logn)操作。维护平衡性由难到易的排列: AVL树 > 2-3树 > 2-4树 ·= 红黑树。其次,随着对于结点含有3个以上的数据元素的查找树的性能反而会降低,因此这也是为什么没有2-5树……的原因。。。
2018.12更新
6, 红黑树与AVL树的对比
不管是红黑树还是AVL树,不仅要有效地支持查找,还需要有效地支持插入和删除,插入和删除操作会改变树的形态,如果不做一定的调整,树的结构就会变得不平衡,因此不管是AVL树还是红黑树都有“旋转”操作。
但是,二者的“旋转”操作的代价是不一样的。在最坏的情况下,AVL树的旋转操作代价能达到O(logN),而红黑树的旋转操作代价为O(1)。
由于插入、删除操作会引发树的旋转,因此:红黑树比AVL树更适合于插入、删除元素更频繁的情形,而AVL树更适合于查询非常多,插入、删除很少的场景。
另外,红黑树在JDK集合框架中应用广泛,比如HashMap就引入了红黑树,可以让HashMap在存在大量的键冲突的情况下,仍然能够保证快速地查询。
在正常情况下,HashMap的get操作时间复杂度为O(1),在存在大量冲突时,get操作时间复杂度退化为O(logN)
AVL树的完整JAVA代码实现:
package searchtree; import tree.BinaryNode;
import tree.BinaryNodeInterface; public class AVLTree<T extends Comparable<? super T>> extends BinarySearchTree<T> implements SearchTreeInterface<T>,java.io.Serializable {
public AVLTree(){
super();
} public AVLTree(T rootEntry){
super(rootEntry);
} /*
*@Task : 在AVL树中添加元素
*/
public T add(T newEntry){
T result = null;
if(isEmpty())
setRootNode(new BinaryNode<T>(newEntry));
else
{
BinaryNodeInterface<T> rootNode = getRootNode();
result = addEntry(rootNode, newEntry);
setRootNode(rebalance(rootNode));
}
return result;
} private T addEntry(BinaryNodeInterface<T> rootNode, T newEntry){
assert rootNode != null;
T result = null;
int comparison = newEntry.compareTo(rootNode.getData());//待添加元素与树中已有元素比较以确定添加的位置
if(comparison == 0){//待添加元素已存在于树中
result = rootNode.getData();
rootNode.setData(newEntry);//将新元素替换旧元素
}
else if(comparison < 0){//添加到左子树中
if(rootNode.hasLeftChild()){//继承递归比较
BinaryNodeInterface<T> leftChild = rootNode.getLeftChild();
result = addEntry(leftChild, newEntry);
rootNode.setLeftChild(rebalance(leftChild));
}
else
rootNode.setLeftChild(new BinaryNode<T>(newEntry));
}
else//添加到右子树中
{
assert comparison > 0;
if(rootNode.hasRightChild()){
BinaryNodeInterface<T> rightChild = rootNode.getRightChild();
result = addEntry(rightChild, newEntry);
rootNode.setRightChild(rebalance(rightChild));
}
else
rootNode.setRightChild(new BinaryNode<T>(newEntry));
}
return result;
} public T remove(T newEntry){
return null;//暂未实现删除操作
} /*
* @Task: 在 nodeN 结点上进行右旋操作
*/
private BinaryNodeInterface<T> rotateRight(BinaryNodeInterface<T> nodeN){
BinaryNodeInterface<T> nodeL = nodeN.getLeftChild();
nodeN.setLeftChild(nodeL.getRightChild());
nodeL.setRightChild(nodeN);
return nodeL;
} private BinaryNodeInterface<T> rotateLeft(BinaryNodeInterface<T> nodeN){
BinaryNodeInterface<T> nodeR = nodeN.getRightChild();
nodeN.setRightChild(nodeR.getLeftChild());
nodeR.setLeftChild(nodeN);
return nodeR;
} private BinaryNodeInterface<T> rotateRightLeft(BinaryNodeInterface<T> nodeN){
BinaryNodeInterface<T> nodeR = nodeN.getRightChild();
nodeN.setRightChild(rotateRight(nodeR));
return rotateLeft(nodeN);
} private BinaryNodeInterface<T> rotateLeftRight(BinaryNodeInterface<T> nodeN){
BinaryNodeInterface<T> nodeL = nodeN.getLeftChild();
nodeN.setLeftChild(rotateLeft(nodeL));
return rotateRight(nodeN);
} private BinaryNodeInterface<T> rebalance(BinaryNodeInterface<T> nodeN){
int heightDifference = getHeightDifference(nodeN);
if(heightDifference > 1){//左子树比右子树高
if(getHeightDifference(nodeN.getLeftChild()) > 0)
nodeN = rotateRight(nodeN);//插入在左子树的左孩子中
else
nodeN = rotateLeftRight(nodeN);
}
else if(heightDifference < -1){//右子树比左子树高
if(getHeightDifference(nodeN.getRightChild()) < 0)
nodeN = rotateLeft(nodeN);//插入在右子树的右孩子中
else
nodeN = rotateRightLeft(nodeN);
}
return nodeN;
} //获得结点nodeN的左右子树的高度之差
private int getHeightDifference(BinaryNodeInterface<T> nodeN){
int leftHeight = 0;
int rightHeight = 0;
if(nodeN.getLeftChild() != null){
leftHeight = nodeN.getLeftChild().getHeight();
}
if(nodeN.getRightChild() != null){
rightHeight = nodeN.getRightChild().getHeight();
}
return leftHeight - rightHeight;
}
}
整个实现的依赖代码参考:https://github.com/hapjin/data-structures-and-abstraction-with-java 中的tree、searchtree、list 目录下代码。
AVL树的JAVA实现及AVL树的旋转算法的更多相关文章
- AVL树之 Java的实现
AVL树的介绍 AVL树是高度平衡的而二叉树.它的特点是:AVL树中任何节点的两个子树的高度最大差别为1. 上面的两张图片,左边的是AVL树,它的任何节点的两个子树的高度差别都<=1:而右边的不 ...
- B树,B+树,红黑树应用场景AVL树,红黑树,B树,B+树,Trie树
B B+运用在file system database这类持续存储结构,同样能保持lon(n)的插入与查询,也需要额外的平衡调节.像mysql的数据库定义是可以指定B+ 索引还是hash索引. C++ ...
- AVL树,红黑树,B-B+树,Trie树原理和应用
前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作 ...
- B树、B+树、红黑树、AVL树
定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2. ...
- AVL树、红黑树以及B树介绍
简介 首先,说一下在数据结构中为什么要引入树这种结构,在我们上篇文章中介绍的数组与链表中,可以发现,数组适合查询这种静态操作(O(1)),不合适删除与插入这种动态操作(O(n)),而链表则是适合删除与 ...
- AVL树与红黑树(R-B树)的区别与联系
AVL树(http://baike.baidu.com/view/593144.htm?fr=aladdin),又称(严格)高度平衡的二叉搜索树.其他的平衡树还有:红黑树.Treap.伸展树.SBT. ...
- 从二叉搜索树到AVL树再到红黑树 B树
这几种树都属于数据结构中较为复杂的,在平时面试中,经常会问理解用法,但一般不会问具体的实现,所以今天来梳理一下这几种树之间的区别与联系,感谢知乎用户@Cailiang,这篇文章参考了他的专栏. 二叉查 ...
- B树、B+树、红黑树、AVL树比较
B树是为了提高磁盘或外部存储设备查找效率而产生的一种多路平衡查找树. B+树为B树的变形结构,用于大多数数据库或文件系统的存储而设计. B树相对于红黑树的区别 在大规模数据存储的时候,红黑树往往出现由 ...
- PAT-1066(Root of AVL Tree)Java语言实现
Root of AVL Tree PAT-1066 这是关于AVL即二叉平衡查找树的基本操作,包括旋转和插入 这里的数据结构主要在原来的基础上加上节点的高度信息. import java.util.* ...
随机推荐
- Qt__QWidget::update()与Qwidget::repaint()的区别
QT事件的产生 1.操作系统产生 操作系统将获取的事件,比如鼠标按键,键盘按键等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEv ...
- Linux下DB2命令学习及整理
DB2相关数据库命令 1.数据库实例的启动首先要启动数据库的实例,即切换到db2inst1用户(注:db2inst1用户为当前数据库的实例),然后执行db2start启动数据库的实例 [root@lo ...
- 关于python 文件操作os.fdopen(), os.close(), tempfile.mkstemp()
嗯.最近在弄的东西也跟这个有关系,由于c基础渣渣.现在基本上都忘记得差不多的情况下,是需要花点功夫才能弄明白. 每个语言都有相关的文件操作. 今天在flask 的例子里看到这样一句话.拉开了文件操作折 ...
- Bootstrap导航
前面的话 导航对于一位前端人员来说并不陌生.可以说导航是一个网站重要的元素组件之一,便于用户查找网站所提供的各项功能服务.本文将详细介绍Bootstrap导航 基础样式 Bootstrap框架中制作导 ...
- 美图美妆由Try Try接手运营
美图又把一个拖累营收的业务转让出去了. 美图的电商业务——美图美妆应用在向用户发布终止运营的公告后,宣布把业务交给了寺库旗下公司 Try Try 运营.Try Try 接手了美图美妆的所有管理运营权, ...
- 01 Zabbix采集数据方式
Zabbix采集数据方式 1. zabbix采集数据方式: 基于专用agent 被监控的设备上面安装agent软件,这个agent必须在设备上面有采集数据的权限 基于SNMP, net-snmp ...
- 自学Zabbix5.1 zabbix maintenance维护周期
自学Zabbix5.1 zabbix maintenance维护周期 1. 概述 你可以定义维护周期在主机或主机组里.这里有2种维护状态: 依旧收集数据 继续对目标的监控数据的收集 暂停收集数据 ...
- 省选模拟赛第四轮 B——O(n^4)->O(n^3)->O(n^2)
一 稍微转化一下,就是找所有和原树差距不超过k的不同构树的个数 一个挺trick的想法是: 由于矩阵树定理的行列式的值是把邻接矩阵数值看做边权的图的所有生成树的边权乘积之和 那么如果把不存在于原树中的 ...
- 洛谷P3474 KUP-Plot purchase
简要题意: 给你一个n * n的非负矩阵,求问是否有子矩阵满足和在[k, 2k]之间.若有输出方案.n<=2000. 解: 首先n4暴力很好想(废话),然后发现可以优化成n3log2n,但是还是 ...
- A1052. Linked List Sorting
A linked list consists of a series of structures, which are not necessarily adjacent in memory. We a ...