平衡二叉树 AVL树结构详解 [Java实现]

640?wx_fmt=jpeg

作者

NeroJings

来源

https://blog.csdn.net/zhang6622056/article/details/82698859


本文思维导图

640?wx_fmt=png

简述

先不说平衡二叉树,我们单开来说,这样比较方便理解。 

先说二叉树,再说平衡条件,没那么多花里胡哨的理论,我只是想让大家看完能明白,能写出来。

二叉树

什么是二叉树?二叉树数据结构,顾名思义,只有两个叉,在数据结构中,操作性能要远高于线性结构,有O(height)的索引性能。与线性结构有相同的空间复杂度,特性如下: 

  • 每个节点最多只有两个儿子节点

  • 左儿子小,右儿子大 (大小按照我们默认的比较规则,本例用int来比较)

线行找7与二叉数找7

640?wx_fmt=gif

线性找7 


640?wx_fmt=gif

二叉树找7  

okay,我想大家聪明人已经看出来了,二叉树搜索用了2次,而线性结构却用了5次。 

说白了,二叉树结构,我每次问一个节点,都会离着我的目标越来越近,但是线性的则不然,我必须一个个问。 

说到这儿,我想会有博友提出质疑了,如果线性查找,7恰好就在第一个呢?那不是一下就找到了吗? 

哈哈,你怎么不上天呢 - -。还第一个。开个小玩笑。 

这就是二叉树索引的好处。相比看图比码字要清楚的多。

平衡条件

那么,什么叫平衡呢?其实很简单,任何一个节点的子节点高度差必须小于2 

640?wx_fmt=png

第一个二叉平衡树 

  • 从下往上数,第一个高度为1(比较符合日常生活数数),那我们数数吧

  • 5:———1高度 | 4,7,23,71 ———2高度| 6,50 ———3高度 | 15 ———4高度

  • 比如节点6,那么4和7的高度都是2,那就2-2 < 2 。平衡!!

难点一  递归

640?wx_fmt=gif

递归查找 

我又加入了一些节点,方便大家理解递归深度

  • 每一次正向橙色线条的滚动,就是一次递归查找

  • 每一次正向橙色线条的滚动,方法的入栈!

  • 递归的深度,取决于线条走了几次,那就有多大的栈深度

  • 本次查找,刨除root,共4次进栈 

难点二 回溯

640?wx_fmt=gif

插入回溯

先不要关心这个旋转操作,如图所示,我们在递归的基础上,沿着线条理解一下回溯

  • 每一次逆向色线条的滚动,就是一次回溯

  • 操作递归的每一个节点,都会在回溯的轨迹上

  • 正因为每一次递归,都有每一次回溯,那么,我们就可以先完成相关操作(增加或删除)之后,判定平衡 

4种旋转

640?wx_fmt=gif

左左类型旋转

博主尽量放慢了速度,让大家看清楚究竟旋转是如何进行的,这是一个插入操作,我们看到在不平衡的时候,进行了左旋转,这里我们看到

  • 正向插入,递归3-2-1

  • 逆向回溯,1-2 判断平衡条件 ,是平衡的

  • 再次回溯,2-3,3的左边高度为2,右边没有节点为0,那么2-0 > 1,不平衡!

到这里我们基本上理解了平衡的判断,下面正式说一下旋转:

  • 判断不平衡边 在3节点判定,不平衡,那么左边高,我们需要调整左边,获取左边节点2

  • 判断旋转类型 这时候我们拿到节点2,判断节点2哪边高。左边高,为左左类型。右边高为左右旋转类型,我们先不管

  • 旋转操作 3.left = 2.right; 2.right = 3; 重新计算,2和3节点的高度

640?wx_fmt=gif

右右类型旋转[同上,不再叙述]  


640?wx_fmt=gif

左右类型旋转


640?wx_fmt=gif

 右左类型旋转

到此旋转就说完了,希望大家好好的理解第一个左左类型!(理解了一个也就都理解了 

后续部分没有讲是因为说太多反而更乱。 

后续的理解不了没关系,我们代码在看。

代码基础部分

node类

 
 

高度计算

/***     * 求左右子节点最大高度     * @param left     * @param right     * @return     */    private int maxHeight(AvlNodeInteger left,AvlNodeInteger right){        return height(left) > height(right) ? height(left)  : height(right);    }

插入操作

旋转

/***     * 左左旋转模型     * @param node  旋转之前的parent node 节点     * @return  旋转之后的parent node节点     */    private AvlNodeInteger leftLeftRotate(AvlNodeInteger node){        AvlNodeInteger newRoot = node.getLeft();        node.setLeft(newRoot.getRight());        newRoot.setRight(node);        //由此node的高度降低了,newRoot的高度提高了。         //newRoot的高度由node的高度而来        node.setHeight(maxHeight(node.getLeft(),node.getRight())+1);        newRoot.setHeight(maxHeight(newRoot.getLeft(),newRoot.getRight())+1);        return newRoot;    }    /***     * 右右旋转模型     * @param node     * @return     */    private AvlNodeInteger rightRightRotate(AvlNodeInteger node){        AvlNodeInteger newRoot = node.getRight();        node.setRight(newRoot.getLeft());        newRoot.setLeft(node);        //由此node的高度降低了,newRoot的高度提高了。        //newRoot的高度由node的高度而来        node.setHeight(maxHeight(node.getLeft(),node.getRight()));        newRoot.setHeight(maxHeight(newRoot.getLeft(),newRoot.getRight()));        return newRoot;    }    /**     * 左右模型,先右右,再左左     * @param node     * @return     */    private AvlNodeInteger leftRightRotate(AvlNodeInteger node){        //注意传递的参数        node.setLeft(rightRightRotate(node.getLeft()));        return leftLeftRotate(node);    }    /***     * 右左模型,先左左,在右右     * @param node     * @return     */    private AvlNodeInteger rightLeftRotate(AvlNodeInteger node){        node.setRight(leftLeftRotate(node.getRight()));        return rightRightRotate(node);    }

insert

 
 

删除操作

以上就是难点插入和删除的实现了, 没有过多阐述,是因为大家如果真的理解了上面说明的理论, 那么应该没有问题来理解这些code。 

当然有任何问题大家可以在留言区回复我 ,欢迎大家指正!

4种遍历

  • 前序遍历 根左右

  • 中序遍历 左跟右

  • 后序遍历 左右根

  • 层级遍历 从root开始,一层层


/***java     * 前序遍历     * 1-根节点     * 2-左节点     * 3-右节点     * 根左右     * @param parent     */    private void xianxu(AvlNodeInteger parent){        System.out.println(parent.getValue());        if(null != parent.getLeft()){            xianxu(parent.getLeft());        }        if(null != parent.getRight()){            xianxu(parent.getRight());        }    } /***     * 中序遍历     * 左节点     * 根节点     * 右节点     *      * 左根右     * @param parent     */    private void zhongxu(AvlNodeInteger parent){        if(null != parent.getLeft()){            zhongxu(parent.getLeft());        }        System.out.println(parent.getValue());        if(null != parent.getRight()){            zhongxu(parent.getRight());        }    }    /***     * 后续遍历     * 左右根     * 左节点     * 右节点     * 根节点     */    private void houxu(AvlNodeInteger parent){        if(null != parent.getLeft()){            houxu(parent.getLeft());        }        if(null != parent.getRight()){            houxu(parent.getRight());        }        System.out.println(parent);    }    /***     * 层级遍历     * @param parent     */    private void cengji(List<AvlNodeInteger> parent){        if(null == parent || parent.size() == 0) return;        //打印当前层        List<AvlNodeInteger> AvlNodeIntegers = new ArrayList<AvlNodeInteger>();        int k = 0;        for(int i = 0 ; i < parent.size() ; i++){            AvlNodeInteger currentNode = parent.get(i);            System.out.println(currentNode.getValue()+",");            if(null != currentNode.getLeft()){                AvlNodeIntegers.add(currentNode.getLeft());                k++;            }            if(null != currentNode.getRight()){                AvlNodeIntegers.add(currentNode.getRight());                k++;            }        }        System.out.println("--------------------------");        cengji(AvlNodeIntegers);    }

完整源码见:平衡二叉树 AVL树结构详解 [Java实现]--源码部分

END


640?wx_fmt=jpeg

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值