大一就学了数据结构,当听到AVL树的时候就很蒙,也没认真。最近开始认真看,那书上讲的何止是蒙,讲的我是莫名其妙。不知道为什么那么多人喜欢严蔚敏版的数据结构,没觉得好在哪里,讲的少,讲的粗略,讲的也是莫名其妙。可能是我的问题把,所以在网上搜了相关资料的,我一直认为,一个理论要马上接收,其实挺难的,我的做法是先不管那么多,掌握基本的操作就行,在你每次用的过程中,总会关心到某些值得推敲的地方,这样的抛砖引玉,显得理论没那么的生硬;因此,我写博客都是按照操作方法的标准写的,至于理论,百科多如牛毛,写了也不懂;后知后觉,似乎更加容易些。
仅是个人学习经验,有误请指教
这篇博客是我在网上看到的,他也是直接写操作的,并且写的挺明白的,所以我转了下
http://hxraid.iteye.com/blog/609949
平衡二叉树定义(AVL):
它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。
最小不平衡子树:指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。
平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;
插入操作
在平衡二叉树中插入结点与二叉查找树最大的不同在于要随时保证插入后整棵二叉树是平衡的。那么调整不平衡树的基本方法就是: 旋转,基本思路都是转换到左旋和右旋。
1) 右旋: 在最小平衡子树根节点平衡因子>=2且在根节点的左孩子的左孩子插入元素,进行右旋
2) 左旋: 在最小平衡子树根节点平衡因子>=-2且在根节点的右孩子的右孩子插入元素,进行左旋。
3) 右左:最小平衡子树根节点(80)的右孩子(100)的左孩子(90)的子节点(95)插入新元素,先绕根节点的右孩子节点(100)右旋,再围根节点(80)左旋
4) 左右:在最小平衡子树根节点(80)的左孩子(50)的右孩子(70)的子节点插入新元素,先绕根节点的左孩子节点(50)右旋,再围根节点(80)左旋
平衡二叉树性能分析
平衡二叉树的性能优势:
很显然,平衡二叉树的优势在于不会出现普通二叉查找树的最差情况。其查找的时间复杂度为O(logN)。
平衡二叉树的缺陷:
(1) 很遗憾的是,为了保证高度平衡,动态插入和删除的代价也随之增加
平衡二叉树的插入操作代码(平衡旋转)
- package net.hr.algorithm.search;
- /**平衡因子枚举类*/
- enum B
- alanceFactor{
- LH("左子树高"),EH("左右等高"),RH("右子树高");
- private String illustration="";
- private BalanceFactor(String s){
- this.illustration=s;
- }
- public String toString(){
- return this.illustration;
- }
- }
- /**
- * 平衡二叉树结点
- */
- class AVLNode<E extends Comparable<E>>{
- /**结点关键字*/
- E key=null;
- /**结点的平衡因子*/
- BalanceFactor bFactor=BalanceFactor.EH;
- /**结点的直接父亲*/
- AVLNode<E> parent=null;
- /**结点的左右孩子*/
- AVLNode<E> lchild,rchild=null;
- AVLNode(E k){
- this.key=k;
- }
- /**
- * 格式输出结点
- */
- public String toString(){
- //String fomateStr="";
- //if(this.lchild==null)
- String lchildStr=(this.lchild==null)?"null":this.lchild.key.toString();
- String rchildStr=(this.rchild==null)?"null":this.rchild.key.toString();
- return this.key+"[lchild="+lchildStr+",rchild="+rchildStr+"]";
- }
- }
- /**
- * 平衡二叉查找树
- * @author heartraid
- */
- public class AVL<E extends Comparable<E>> {
- /**树根*/
- private AVLNode<E> root=null;
- /**当前树是否变高*/
- public boolean isTaller=false;
- public AVL(){
- }
- public boolean insert(E key){
- System.out.print("插入["+key+"]:");
- if(key==null) return false;
- if(root==null){
- System.out.println("插入到树根。");
- root=new AVLNode<E>(key);
- return true;
- }
- else{
- System.out.print("搜索路径[");
- return insertAVL(key,root);
- }
- }
- private boolean insertAVL(E key,AVLNode<E> node){
- System.out.print(node.key+" —>");
- // 树中存在相同的key,不需要插入
- if(node.key.compareTo(key)==0){
- System.out.println("]. 搜索有相同关键字,插入失败");
- isTaller=false;
- return false;
- }
- else{
- //左子树搜索
- if(node.key.compareTo(key)>0){
- //当前node的左孩子为空,则插入到结点的做孩子并修改结点的平衡因子为LH
- if(node.lchild==null){
- System.out.println("]. 插入到"+node.key+"的左孩子");
- AVLNode<E> newNode=new AVLNode<E>(key);
- node.lchild=newNode; //设置左孩子结点
- newNode.parent=node; //设置父亲结点
- isTaller=true; //树长高了
- }
- //左孩子不为空,则继续搜索下去
- else{
- insertAVL(key,node.lchild);
- }
- //当前如果树长高了,说明是因为左孩子的添加改变了平衡因子(左高)。
- if(isTaller){
- System.out.print(" 树变化了,"+node.key+"的平衡因子变化");
- switch(node.bFactor){
- //原来结点平衡因子是LH(bf=1),则左高以后bf=2,因此需要做左平衡旋转
- case LH: {
- System.out.println("[LH=1 ——> LH=2]. 出现了不平衡现象[左比右高2]");
- System.out.println(" ★ 以"+node.key+"为根将树进行左平衡处理");
- leftBalance(node);
- isTaller=false;
- break;
- }
- //原来结点平衡因子是EH(bf=0),则左高了以后bf=1,不需要平衡处理。
- case EH:{
- System.out.println("[EH=0 ——> LH=1]. 没有不平衡现象");
- node.bFactor=BalanceFactor.LH;
- isTaller=true;
- break;
- }
- //原来结点平衡因子是RH(bf=-1),则左高以后bf=0,不需要平衡处理。
- case RH:{
- System.out.println("[RH=-1 ——> EH=0]. 没有不平衡现象");
- node.bFactor=BalanceFactor.EH;
- isTaller=false;
- break;
- }
- }//end switch
- }//end if
- }//end if
- //右子树搜索
- else{
- if(node.rchild==null){
- System.out.println("]. 插入到"+node.key+"的右孩子");
- AVLNode<E> newNode=new AVLNode<E>(key);
- node.rchild=newNode; //设置右孩子结点
- newNode.parent=node; //设置父亲结点
- isTaller=true; //树长高了
- }
- else{
- insertAVL(key,node.rchild);
- }
- //当前如果树长高了,说明是因为右孩子的添加改变了平衡因子(右高)。
- if(isTaller){
- System.out.print(" 树变化了,"+node.key+"的平衡因子变化");
- switch(node.bFactor){
- //原来结点平衡因子是LH(bf=1),则右高以后bf=0,不需要平衡处理。
- case LH: {
- System.out.println("[LH=1 ——> EH=0]. 没有不平衡现象");
- node.bFactor=BalanceFactor.EH;
- isTaller=false;
- break;
- }
- //原来结点平衡因子是EH(bf=0),则右高了以后bf=-1,不需要平衡处理。
- case EH:{
- System.out.println("[EH=0 ——> RH=-1]. 没有不平衡现象");
- node.bFactor=BalanceFactor.RH;
- isTaller=true;
- break;
- }
- //原来结点平衡因子是RH(bf=-1),则右高以后bf=0,因此需要做右平衡旋转。
- case RH:{
- System.out.println("[RH=-1 ——> RH=-2]. 出现了不平衡现象[左比右矮2]");
- rightBalance(node);
- isTaller=false;
- break;
- }
- }//end switch
- }//end if(isTaller)
- }//end else
- return true;
- }//end else
- }
- /**
- * 左平衡旋转处理
- * 先对node的左子树进行单左旋处理,在对node树进行单右旋处理
- *
- * 100 100 90
- * / \ 左旋 / \ 右旋 / \
- * 80 120 ------> 90 120 ------> 80 100
- * / \ / / \ \
- * 60 90 80 60 85 120
- * / / \
- * 85 60 85
- *
- * @param node 需要做处理的子树的根结点
- */
- private void leftBalance(AVLNode<E> node){
- // node.parent指向新的孩子结点
- AVLNode<E> lc=node.lchild;//lc指向node的左孩子结点
- switch(lc.bFactor){
- case LH:{ //新结点插入在node的左孩子的左子树上,则需要单右旋处理
- System.out.println(" ┖ 对"+node.key+"进行单右旋转处理");
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- rRotate(node);
- break;
- }
- case RH:{ //新结点插入在node的左孩子的右子树上,需要双旋处理
- System.out.println(" ┖ 对"+node.key+"的左子树进行单左旋转处理,再对其本身树进行单右循环处理");
- AVLNode<E> rd=lc.rchild; //rd指向node左孩子的右子树根
- switch(rd.bFactor){ //修改node与其左孩子的平衡因子
- case LH:{
- node.bFactor=BalanceFactor.RH;
- lc.bFactor=BalanceFactor.EH;
- break;
- }
- case EH:{
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- break;
- }
- case RH:{
- node.bFactor=BalanceFactor.EH;
- lc.bFactor=BalanceFactor.LH;
- break;
- }
- }//switch
- rd.bFactor=BalanceFactor.EH;
- lRotate(node.lchild);
- rRotate(node);
- break;
- }
- }
- }
- /**
- * 右平衡旋转处理
- *
- * 80 80 85
- * / \ 右 旋 / \ 左 旋 / \
- * 60 100 ------> 60 85 -------> 80 100
- * / \ \ / / \
- * 85 120 100 60 90 120
- * \ / \
- * 90 90 120
- *
- * @param node
- */
- private void rightBalance(AVLNode<E> node){
- AVLNode<E> lc=node.rchild;//lc指向node的右孩子结点
- switch(lc.bFactor){
- case RH:{ //新结点插入在node的右孩子的右子树上,则需要单左旋处理
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- lRotate(node);
- break;
- }
- case LH:{ //新结点插入在node的右孩子的左子树上,需要双旋处理
- AVLNode<E> rd=lc.lchild; //rd指向node右孩子的左子树根
- switch(rd.bFactor){ //修改node与其右孩子的平衡因子
- case LH:{
- node.bFactor=BalanceFactor.EH;
- lc.bFactor=BalanceFactor.RH;
- break;
- }
- case EH:{
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- break;
- }
- case RH:{
- node.bFactor=BalanceFactor.LH;
- lc.bFactor=BalanceFactor.EH;
- break;
- }
- }//switch
- rd.bFactor=BalanceFactor.EH;
- rRotate(node.rchild);
- lRotate(node);
- break;
- }
- }
- }
- /**
- * 对以node为根的子树进行单右旋处理,处理后node.parent指向新的树根,即旋转之前
- * node的左孩子结点
- * 100<-node.parent 80<-node.parent
- * / / \
- * 80 ———> 60 100
- * / \ /
- * 60 85 85
- */
- private void rRotate(AVLNode<E> node){
- AVLNode<E> lc=node.lchild;//lc指向node的左孩子结点
- node.lchild=lc.rchild;
- lc.rchild=node;
- if(node.parent==null){
- root=lc;
- }
- else if(node.parent.lchild.key.compareTo(node.key)==0)
- node.parent.lchild=lc;
- else node.parent.rchild=lc;
- }
- /**
- * 对以node为根的子树进行单左旋处理,处理后node.parent指向新的树根,即旋转之前
- * node的右孩子结点
- * 100<-node.parent 110<-node.parent
- * \ / \
- * 110 ————> 100 120
- * / \ \
- * 105 120 105
- */
- private void lRotate(AVLNode<E> node){
- AVLNode<E> rc=node.rchild;//lc指向node的右孩子结点
- node.rchild=rc.lchild;
- rc.lchild=node;
- if(node.parent==null){
- root=rc;
- }
- else if(node.parent.lchild.key.compareTo(node.key)==0)
- node.parent.lchild=rc;
- else node.parent.rchild=rc;
- }
- /**
- * 得到BST根节点
- * @return BST根节点f
- */
- public AVLNode<E> getRoot(){
- return this.root;
- }
- /**
- * 递归前序遍历树
- */
- public void preOrderTraverse(AVLNode<E> node){
- if(node!=null){
- System.out.println(node);
- preOrderTraverse(node.lchild);
- preOrderTraverse(node.rchild);
- }
- }
- /**
- * 测试
- * @param args
- */
- public static void main(String[] args) {
- AVL<Integer> avl=new AVL<Integer>();
- avl.insert(new Integer(80));
- avl.insert(new Integer(60));
- avl.insert(new Integer(90));
- avl.insert(new Integer(85));
- avl.insert(new Integer(120));
- avl.insert(new Integer(100));
- System.out.println("前序遍历AVL:");
- avl.preOrderTraverse(avl.getRoot());
- }
- }
相关问题1:N层平衡二叉树至少多少个结点
假设F(N)表示N层平衡二叉树的结点个数,则F[1]=1,F[2]=2。而F(N)=F(N-2)+F(N-1)+1
为什么呢?我们可以这样考虑,假设现在又一个(N-2)层和(N-1)层的最少结点平衡二叉树。要构造一棵N层的平衡二叉树,则只需加入一个根节点,其左右子树分别(N-2)层和(N-1)层的树即可 。由于两个子树都是最少结点的,所有N层的也是最少结点的。