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

AVL树概念

前面学习二叉查找树二叉树的各类遍历,可是其查找效率不稳定(斜树),而二叉平衡树的用途更多。查找相比稳定不少。(欢迎关注数据结构专栏)java

  • AVL树是带有平衡条件的二叉查找树。这个平衡条件必需要容易保持。并且要保证它的深度是O(logN).
  • AVL的条件是左右树的高度差(平衡因子)不大于1;而且它的每一个子树也都是平衡二叉树。
  • 对于平衡二叉树的最小个数,n0=0;n1=1;nk=n(k-1)+n(k-2)+1;(求法能够类比斐波那契!)

难点:AVL是一颗二叉排序树,用什么样的规则或者规律让它可以在复杂度不过高的状况下实现动态平衡呢?
在这里插入图片描述node

不平衡概况

在这里插入图片描述
若是简单的以单节点看,大体有上面四种情形,而且他们的最后结果也是有的有所相近。只是:上下会变更。该在左面的还在左面,改在右面的还在右面
在这里插入图片描述
这只是针对在底部,对于可能出现的平衡要首先搞清楚:
在这里插入图片描述
因此针对四种不平衡,可能出如今底部,也可能出如今头,也可能出如今某个中间节点致使不平衡。 而咱们只须要研究其首次不平衡点,解决以后整棵树即继续平衡。固然,在实际解决确定会带上递归的思想解决问题。算法

# 四种平衡旋转方式

RR平衡旋转(左单旋转)

在这里插入图片描述
出现这种状况的缘由是节点的右侧的右侧较深这时候不平衡节点须要左旋。再细看过程。后端

  • 再左旋的过程当中,root(oldroot)节点下沉,中间节点(newroot)上浮.而其中中间节点(newroot)的右侧依然不变。
  • 它上浮左侧因此须要指向根节点(oldroot)(毕竟一棵树)。可是这样newroot原来左侧节点H空缺。而咱们须要仍然让整个树完整而且知足二叉排序树的规则
  • 而恰好原本oldroot右侧指向newroot变成oldroot被newroot左侧指向。因此oldroot右侧空缺,恰好这个位置知足在oldroot的右侧。在newroot的左侧。.因此咱们将H插入在这个位置。
  • 其中H可能为NULL。不过不影响操做!
    在这里插入图片描述
    而左旋的代码能够表示为:
private node getRRbanlance(node oldroot) {//右右深,须要左旋
    // TODO Auto-generated method stub
    node newroot=oldroot.right;
    oldroot.right=newroot.left;
    newroot.left=oldroot;
    oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
    newroot.height=Math.max(getHeight(newroot.left),getHeight(newroot.right))+1;//原来的root的高度须要重新计算
    return newroot;     
}

LL平衡旋转(右单旋转)

而右旋和左旋相反,可是思路相同,根据上述进行替换便可!
在这里插入图片描述
代码:数据结构

private node getLLbanlance(node oldroot) {//LL小,须要右旋转
    // TODO Auto-generated method stub
    node newroot=oldroot.left;
    oldroot.left=newroot.right;
    newroot.right=oldroot;
    oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
    newroot.height=Math.max(getHeight(newroot.left),getHeight(newroot.right))+1;//原来的root的高度须要重新金酸  
    return newroot; 
}

RL平衡旋转(先右后左双旋转)

产生不平衡的条件缘由是:学习

  • root节点右侧左侧节点的深度高些,使得与左侧的差大于1.这个与咱们前面看到的左旋右旋不一样的是由于它的结构不能直接变一下就能够完成。
  • 由于对于右左结构,中间的最大,两侧的最小。可是下面的比上面大(下面在上面右侧)因此若是平衡的话,那么右左的R.L应该在中间,而R应该在右侧。原来的root在左侧。
  • 因此节点的变化浮动比较大,并且须要妥善处理各个子节点的移动使其知足二叉排序树的性质!
  • 期间考虑树高度变化便可!

这种双旋转其实也很简单。不要被外表唬住。基于前面的单旋转,双旋转有两种具体逻辑思路
思路1:两次旋转RR,LL
在这里插入图片描述
根据上图所圈的,先对底部使得底部的大小关系变化,使其在知足二叉平衡树的条件下还知足RR结构的二叉树。因此只须要对右节点R先进行右旋,再对ROOT进行左旋便可。
思路2:直接分析
根据初始和结果的状态,而后分析各个节点变化顺序。手动操做这些节点便可!测试

  • 首先根据ROOT,R,R.L三个节点变化。R.L确定要在最顶层。左右分别指向ROOT和R。那么这其中R.left,ROOT.right发生变化(原来分别是R,L和R)暂时为空。而恰好根据左右大小关系能够补上R.L的左右节点
  • 这样思考整棵树也能够完成平衡,可是要考虑树的高度变化
    在这里插入图片描述
    代码为:(注释部分为方案1)
private node getRLbanlance(node oldroot) {//右左深 
//      node newroot=oldroot.right.left;
//      oldroot.right.left=newroot.right;
//      newroot.right=oldroot.right;
//      oldroot.right=newroot.left; 
//      newroot.left=oldroot;
//      oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
//      newroot.right.height=Math.max(getHeight(newroot.right.left),getHeight(newroot.right.right))+1;
//      newroot.height=Math.max(getHeight(oldroot.left),getHeight(newroot.right))+1;//原来的root的高度须要重新金酸  
    oldroot.right =getLLbanlance(oldroot.right);
    oldroot.height=Math.max(getHeight(oldroot.left), getHeight(oldroot.right))+1;
    return getRRbanlance(oldroot);
        
    }

LR平衡旋转(先左后右单旋转)

根据上述RL修改便可
在这里插入图片描述.net

private node getLRbanlance(node oldroot) {
    oldroot.left =getRRbanlance(oldroot.left);
    oldroot.height=Math.max(getHeight(oldroot.left), getHeight(oldroot.right))+1;
    return getLLbanlance(oldroot);
        
    }

java代码实现

  • 首先对于节点多个height属性。用于计算高度(平衡因子)
  • 插入是递归插入。递归一个来回的过程,去的过程进行插入。回的过程进行高度更新。和检查是否平衡。不要写全局递归计算高度,效率过低下。事实上高度变化只和插入和平衡有关,仔细考虑即不会有疏漏!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述code

    总结

    测试状况:
    在这里插入图片描述
  • AVL的理解须要时间,固然笔者的AVL本身写的可能有些疏漏,若是有问题还请各位一块儿探讨
  • 固然,除了插入,AVL还有删除等其余操做,(原理类似。删除后平衡)有兴趣能够一块儿研究。
  • 若是须要源码还请关注笔者公众号:公众号查看相关专题文章!
  • 若是对后端、爬虫、数据结构算法等感性趣欢迎关注个人我的公众号交流:bigsai(回复数据结构、爬虫、java等有精心准备资料一份!)
    blog

相关文章
相关标签/搜索