AVL树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AVL树相关的知识,希望对你有一定的参考价值。

  日前,马自达汽车株式会社发布了展望2030年马自达技术开发的长期愿景——“Zoom-Zoom 可持续发展宣言2030”。为实现这一愿景,马自达宣布将于2019年推出包含全新一代“SKYACTIV-X”发动机在内的一系列革新性技术。而“SKYACTIV-X”发动机,则是在全球范围内首次将压燃点火技术进行产品化实际应用的汽油发动机。
  
  基于2007年3月公布的技术开发长期愿景“Zoom-Zoom 可持续发展宣言”,马自达一直以来致力于打造兼具“驾乘愉悦”及“出色环保、安全性能”的优质汽车产品。而此次,马自达直指全球汽车产业环境的激变,站在更加长远的视角和高度,通过彰显汽车产品“驾乘愉悦”的独有魅力,解决当今“地球”、“社会”、“人”三方各自课题的全新挑战,发布“Zoom-Zoom可持续发展宣言2030”这一全新技术开发长期愿景。
  
  首先,对“地球”领域的思考,马自达认为针对削减全球变暖主因的温室效应气体排放,需要实质性地减少二氧化碳排放。马自达将针对今后仍将在全球范围内占据绝大多数的、对减少目前二氧化碳排放量最为有效的内燃机高效能化这一终极理想进行持续的研发和探求,同时推出匹配极具效率的电动化技术组合方案。
  
  此外,针对应用清洁发电技术及以控制大气污染为宗旨对机动车实施相关限制性法规的地区,将于2019年开始引入电动车等电气驱动技术。
  
  以创建“零事故安全汽车社会”为目标,马自达将通过继续改进追求正确理想的驾驶位置和姿态、踏板布局、良好的视野及视认性能等基本安全技术来实现这一目标。将这些先进安全技术性能的持续升级和标配化配备普及的同时,马自达希望能在2025年将基于以人为本的、让驾驶者能够安心享受驾乘愉悦的“Mazda Co-Pilot Concept”自动驾驶理念的自动驾驶技术实现标准化配备。
  
  最后,除了对“地球”及“社会”所作出的贡献之外,马自达也将想办法改善“人”的日常生活。马自达汽车株式会社社长兼CEO小飼雅道表示,未来,马自达将把“Zoom-Zoom 可持续发展宣言2030”内提及的技术逐序地应用到相关车型产品中。具体地说,马自达将于今年内,举办针对新一代汽油发动机“SKYACTIV-X”及新一代平台什么是AVL树?

AVL树,又称为平衡二叉树,它是一种特殊的二叉查找树(Binary Search Tree, BST),其每一个节点的左右子树的高度差不超过1。

注意,一个节点的高度是从该节点到叶子节点的最长路径,所以,叶子节点的高度为0,而深度是指一个节点到树根的路径长度,两者是相反的概念。 一棵树的高度等于根节点的高度,而深度等于最大深度的叶子节点的深度,所以一个树的高度和深度是相同的。


二叉查找树的定义是递归的:1)左子树所有节点的值都比根节点小,右子树所有的节点都比根节点大,2)左子树和右子树都是二叉查找树。

当所有的插入序列都是等可能的情况下,二叉查找树的平均深度是O(log N),但是当遇到极端情况(比如插入有序的序列)或者多次的插入\删除操作会使得二叉查找树的深度变大,而平衡二叉树加大了对二叉查找树的限制:任何一个节点的左右子树的高度差不能超过1,这就保证了平衡二叉树的深度为O(log N),使得平衡二叉树的最坏的查找效率是O(log N),而二叉查找树的最坏查找效率可以是O(N)。那么问题的关键就是如何在插入和删除的时候保持平衡二叉树的性质。

如何在插入一个节点的时候保持树的平衡?

当插入一个新的节点的时候,如果插入的新节点使得一些节点的左右子树的高度差等于了2,那么这时候就需要旋转(rotation)来调整不平衡的子树,使得整个树仍然保持平衡二叉树的性质。导致不平衡插入只有四种情况:

左左,即插入点为不平衡节点的左儿子的左子树中,使得原来平衡节点的左子树比右子树高1,变成现在变成高2了。
右右,即插入点为不平衡节点的右儿子的右子树中,使得原来平衡节点的右子树比左子树高1,变成现在变成高2了,他是第一种情况的对称情况
左右,即插入点为不平衡节点的左儿子的右子树中,使得原来平衡节点的左子树比右子树高1,变成现在变成高2了。
右左,即插入点为不平衡节点的右儿子的左子树中,使得原来平衡节点的右子树比左子树高1,变成现在的高2,它是第三种情况的堆成情况。
我们先看前两种情况的处理方式(单旋转):

左左的情况如下图1所示, k1为不平衡节点,由于新节点的插入到了X子树,使得k1的左子树的高度比右子树的高度差2,这时候,我们只需要做单旋转操作:1)把k1的右孩子变成k2(k1的左孩子)的右子树,2)把k2的左孩子变成k1,返回平衡后的树根k2。相同的图2显示了右右单旋转的操作过程


图1:左左单旋转



图2:右右单旋转
如果不平衡情况是第3,4种,那么单选并不能使得树变平衡,以第3种情况为例,我们使用单旋转来改变树的形态,其过程如图3所示,显然,单旋转不能使得左右的不平衡的状态达到平衡。


图3:单旋转不能处理左右不平衡的情况
那么第三种和第四种情况如何处理呢,我们不能再把k2提上去作为树的根了,那么如果我们把Y子树的根 作为新的树根呢,这是一个可行的方案,这就是双旋转最初的想法。

以第三种情况为例(见图4),也就是插入点位于不平衡节点的左孩子的右子树上,我需要两次旋转来调整树达到平衡,首先将k3(k2的右孩子)左旋转到k2的位置,然后在将k2通过单旋转到k1的位置,整个过程如图4所示,红线为需要变动的链接。其中,在k3左旋转到k2位置上时需要将k3的左子树变成k2的右子树,在k3右转到k1位置的时候需要将k3的右子树变成k1的右子树。图5展示了右左不平衡的情况的双旋转。


图4:左右双旋转



图5:右左双旋转
Java AVL树插入的实现

在上面的解释中,我们有一个重要的问题没有解决,也就是在插入后如何确定不平衡点。实际上,我们在插入后需要进行回溯去找到不平衡的父节点,回溯的方法有多种,比如用栈存储插入节点对比的路径,从栈中取出存储信息,另一种方法是使用递归,其实也是隐式的使用栈的操作,再用一种是在树的结构中保持父节点的引用,但是这会占用更多的内存。我的实现使用了栈的操作,接着后面分析了<数据结构与算法分析-Java语言描述>一书中递归的实现,后者的实现更巧妙,是值得推荐的操作,我自己的使用栈的实现原则上是和它一样的,但是它能跟让我深刻的理解递归背后的详细操作。
相同的,我们也可以写出旋转一个节点的右孩子到父节点的位置

描述:旋转一个节点的右孩子到父节点位置
输入:待旋转节点k1,
输出:旋转后的树根
k2=k1的右孩子
让k1的右指针指向k2的的左孩子
让k2左指针指向k1
更新k1的高度信息为max(k1左孩子的高度,k1右孩子的高度)+1
更新k2的高度信息为max(k1的高度,k2右孩子孩子的高度)+1

那么我们可以轻易的写出这两个函数:

private int height(AvlNode<T>node) {//高度函数,在节点为null的时候返回-1
return node==null?-1:node.height;
}
private AvlNode<T> rotateWithLeftChild(AvlNode<T> k1){
AvlNode<T> k2=k1.left;
k1.left=k2.right;//k1的左子树换成k2的右子树
k2.right=k1;//k2的右子树变k1
k1.height=Math.max(height(k1.left), height(k1.right))+1;
k2.height=Math.max(height(k2.left), k1.height)+1;//k1现在是k2的右子树根
return k2;
}

private AvlNode<T> rotateWithRightChild(AvlNode<T> k1){
AvlNode<T> k2=k1.right;
k1.right=k2.left;
k2.left=k1;
k1.height=Math.max(height(k1.left), height(k1.right))+1;

单旋转操作只需要调用上面的单次旋转操作即可,而双旋转需要两次旋转操作,以左右不平衡来说,我们的解决思路如一下伪代码:

描述:左右不平衡条件下的双旋转操作
输入:不平衡节点k1
输出:新的平衡树根
k2=k1的左孩子
k1的左孩子=旋转k2的右孩子到k2的位置后的新树根(rotateWithRightChild(k2))
newRoot=旋转k1的左孩子到k1的位置后的新树根(rotateWithLeftChild(k1))
返回newRoot

7
相信看了这个伪代码,你也可以很容易的写出右左不平衡条件下的双旋转操作,这里就不再写出。
其代码实现如下:

private AvlNode<T> doubleRotateWithLeftChild(AvlNode<T www.wmyl20.com > k1){
AvlNode<T> k2=k1.left;
k1.left=rotateWithRightChild(k2);
return rotateWithLeftChild(k1);
}
实现我们用一个叫做balance的函数实现,其实现过程遵循前面的算法

public AvlNode<T> balance(AvlNode<T> k1){

另外一种是使用递归的方式来进行插入平衡,其核心思想如下:

描述:向一个树中插入一个节点
输入:插入节点值为x, 待插入树的树根root
输出:插入节点x的后的平衡二叉树根
if root为null
返回新节点newNode(x)
if x>root的值
root的右孩子=以root的右孩子作为树根插入x
if x<root的值
root的左孩子=以root的左孩子作为树根插入x
newRoot=balance当前的树根至平衡节点

代码实现很简洁

public AvlNode<T> insertRecursive(T x, AvlNode<T>root){
if(root==null) {
return new AvlNode<T>(x);
}

if(x.compareTo(root.element)>0)
root.right=insertRecursive(x, root.right);

测试代码,插入31个有序Integer数字,并且按层打印,看其功能性,代码如下:

public static void main(String[] args) {
int MAX=1000000;
long current=System.currentTimeMillis();
AvlTree<Integer>avlTree=new AvlTree<>(www.senta77.com/);
for(int i=1;i<=MAX;i++) {
avlTree.insert(i);
}
long end=System.currentTimeMillis();
System.out.println("建立平衡二叉树时间:"+(end-current)+"ms");
//层序遍历按层打印该树
Queue<AvlNode<Integer>>queue=new LinkedList<>();
int cur=1;//当前层需要遍历的个数
int next=0;//下一次需要遍历的个数
queue.offer(avlTree.getRoot());
while(!queue.isEmpty()) {
AvlNode<Integer>curNode=queue.poll();
System.out.print(curNode.element+" ");
cur--;
if(curNode.left!=null) www.6788878.cn/{
queue.offer(curNode.left);
next++;
}
if(curNode.right!=null) {
queue.offer(curNode.right);
next++;
}

if(cur==0) {

运行结果如图6所示,2微秒建立的平衡二叉树,可以看出有序插入点情况下建立的是完全二叉树。


图6:测试结果

两种方式均能正确的完成平衡二叉树的插入,那么时间性能又如何呢,我们插入100,0000个数字做对比。结果是递归为1039ms,非递归为1263ms,两者相差不大,在平衡二叉树中递归的方式更高效一点。
Java AVL树删除的实现

在探讨AVL树的删除之前,我们先谈论二叉查找树的删除,其实二叉查找树的删除的解决的主要问题是:在删除掉指定节点后如何将被删除节点再连接到被删除节点的父节点上,使其继续保持二叉查找树的性质。在删除的过程中,我们总共可能会遇到三种情况,解决了这三种情况下的删除,也就解决了主要问题。

待删除节点的左右子树为空。这种情况直接删除待删除节点,并且置待删除节点为null
待删除节点有左儿子或右儿子。直接用左儿子或者右儿子代替待删除节点的位置
待删除节点既有左儿子又有右儿子。做法是将待删除节点换成其右子树上节点最小的值,并删除最小值的节点,这种做法的考量是待删除节点的右子树的最小值一定没有左孩子,这样可以在删除该节点的时候更容易。
按照上面的想法,我们给出二叉查找树删除一个节点的递归过程的伪代码:

描述:在二叉查找树上删除一个节点
输入:删除的节点值x,待删除节点二叉查找树的树根root
输出:删除节点后二叉查找树的树根
if t=null
未找到,直接返回null
if x>root的值
x在右子树上
root.right=以root.right为树根删除x后的新树根
else if x<root的值
x在左子树上
root.left=以root.left为树根删除x后的新树根
//开始x=root的值
else if root.left==null并且root.www.zbcppt.com right==null
直接返会null
else if root.left!=null并且root.right!=null
root的值=root.right上的最小值
其实现Java实现如下:

public AvlNode<T> remove(T x, AvlNode<T>root){
if(root==null)
return null;
if(x.compareTo(root.element)>0) {
root.right=remove(x,root.right);
}else if(x.compareTo(root.element)<0) {
root.left=remove(x,root.left);
}else if(root.left!=null&&root.right!=null) {
root.element=findMin(root.right).element;
root.right=remove(root.element,root.right);
}else {
root=(root.left==null?root.right:root.left);
//这种情况涵盖了左右子树都为空和只有左子树或右子树的情况
//如果都为空,那么会root会返回null,如果 左儿子为空,则会返回右孩子的引用
}

17
了解了二叉查找树的删除情况及其实现方式,其实我们很容易想到如何删除AVL树中的一个节点,其实前面的步骤都是相似的,我们只需要在每次删除后进行balance调整,由于整个过程是个递归的过程,所以最终在删除结束后会进行类似插入一样的回溯过程。整个Java实现只需要将最后一行return root,变成return balance(root),其代码如下

public AvlNode<T> remove(T x, AvlNode<T>root){
if(root==null)
return null;
if(x.compareTo(root.element)>www.7881188.cn/ 0) {
root.right=remove(x,root.right);
}else if(x.compareTo(root.element)<0) {
root.left=remove(x,root.left);
}else if(root.left!=null&&root.right!=null) {
root.element=findMin(root.right).element;
root.right=remove(root.element,root.right);
}else {www.baqist.cn/
root=(root.left==null?root.right:root.left);
//这种情况涵盖了左右子树都为空和只有左子树或右子树的情况
//如果都为空,那么会root会返回null,如果 左儿子为空,则会返回右孩子的引用
测试我们建立1-31的二叉平衡树,然后删除8,结果如下,可以看到,8删除的位置被9顶底,10的左子树没了:、新一代设计概念的新闻发布会。相关内容计划将在今年东京车展进行公布及公开展示。第二步,在2018年,马自达计划公布电动化及全新车载互联技术。第三步,在2019年,公布新一代柴油发动机及插电式混合动力汽车(PHEV)技术。而在2020年,马自达计划举办关于自动驾驶技术“Mazda Co-Pilot Concept”的说明会,以及就这四年中全部最新成果的全面系统的概括总结。

Finally, in addition to the "Earth" and "social contribution", Mazda will also try to improve people‘s daily life. Mazda Motor Corporation President and CEO kogai Yadao said, the future, Mazda will put "Zoom-Zoom Declaration on sustainable development 2030" mentioned in the order by technology applied to models related products. Specifically, Mazda will be held this year, according to a new generation of gasoline engine "SKYACTIV-X" and a new generation platform, a new generation of design concepts of the press conference. The content plan will be announced and publicly www.wmyl20.com displayed at this year‘s Tokyo motor show. The second step, in 2018, Mazda announced plans and new electric vehicle interconnection technology. The third step is to announce a new generation of diesel engines and plug-in hybrid www.zbcppt.com electric vehicle (PHE www.baqist.cn/ V) technology in 2019. In 2020, Mazda plans to hold a "Mazda Co-Pilot Concept automatic driving technology" that will, as well as the overall system in these four years all the latest achievements summary.

以上是关于AVL树的主要内容,如果未能解决你的问题,请参考以下文章

AVL树介绍与实现

AVL树介绍与实现

C++AVL树(四种旋转方式)

C++AVL树(四种旋转方式)

C++AVL树(四种旋转方式)

探索 AVL 树基础原理