数据结构-树系列之平衡二叉树(三)

前文也提到了,二叉搜索树一定程度上可以提高搜索效率,但是当原序列有序,例如序列A = {1,2,3,4,5,6},构造二叉搜索树如图3.1。依据此序列构造的二叉搜索树为右斜树,同时二叉树退化成单链表,搜索效率降低为O(n)。所以二叉搜索树的效率跟树的形态密切相关!
在这里插入图片描述在此二叉搜索树中查找元素6需要查找6次。
二叉搜索树的查找效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列A,改为图3.2方式存储,查找元素6时只需比较3次,查找效率提升一倍。
在这里插入图片描述可以看出当节点数目一定,保持树的左右两端保持平衡,树的查找效率最高。这种左右子树的高度相差不超过1的树为平衡二叉树。
在这里插入图片描述在这里插入图片描述

基本概念

所谓平衡二叉树是指它除了具备二叉排序树的基本特性之外,还具有一个非常重要的特点:它的左子树与右子树的深度之差(平衡因子)的绝对值不超过1,且都是平衡二叉树。二叉树结点的左子树深度减去右子树深度的值称为平衡因子。那么平衡二叉树上的所有结点的平衡因子只可能是-1,0,1。只要二叉树上一个结点的平衡因子的绝对值大于1,那么该二叉树就不是平衡二叉树。

平衡因子

定义:某节点的左子树与右子树的高度(深度)差即为该节点的平衡因子(BF,Balance Factor),平衡二叉树中不存在平衡因子大于1的节点。在一棵平衡二叉树中,节点的平衡因子只能取-1、1或者0。

失衡的四种状态

在这里插入图片描述LL:在第一个平衡因子>1的节点的左子树的左子树上插入新节点
LR:在第一个平衡因子>1的节点的左子树的右子树上插入新节点
RL:在第一个平衡因子>1的节点的右子树的左子树上插入新节点
RR:在第一个平衡因子>1的节点的右子树的右子树上插入新节点

插入

面对一颗平衡二叉搜索树,我们在插入的时候就不能简单先搜索再插入了,还得考虑树的平衡。因为要考虑平衡,其实也就是要解决失衡,对应上面那幅失衡图,我们有四种对策来解决失衡问题。

四种旋转
单旋转:左旋转右旋转

单旋转是针对于LL和RR这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。图4是左左情况的解决方案,节点k1不满足平衡特性,因为它的左子树k2比右子树Z深2层,而且k2子树中,更深的一层的是k2的左子树X子树,所以属于左左情况。
在这里插入图片描述开始时,k2节点左孩子是小x,整棵树还是平衡树,然后在小x的子节点插入一个数,小x变成大X,此时大X高度为2,k1节点不平衡,为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k1置于k2的右子树上,而原本在k2右子树的Y大于k1,小于k2,就把Y置于k1的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。

这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。整棵树的新高度和之前没有在左子树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根节点的路径就不需要继续旋转了。

右旋转代码:

 /*
      * @Task: 在 nodeN 结点上进行右旋操作
      */
     private BinaryNodeInterface<T> rotateRight(BinaryNodeInterface<T> nodeN){
         BinaryNodeInterface<T> nodeL = nodeN.getLeftChild();
         nodeN.setLeftChild(nodeL.getRightChild());
         nodeL.setRightChild(nodeN);
         return nodeL;
     }
双旋转:左右旋转,右左旋转

对于LR和RL这两种情况,单旋转不能使它达到一个平衡状态,要经过两次旋转。双旋转是针对于这两种情况的解决方案,同样的,这样两种情况也是对称的,只要解决了左右这种情况,右左就很好办了。图4是左右情况的解决方案,节点k3不满足平衡特性,因为它的左子树k1比右子树D深2层,而且k1子树中,更深的一层的是k1的右子树k2子树,所以属于左右情况。
在这里插入图片描述为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次左旋转,旋转之后就变成了左左情况,所以第二步再进行一次右旋转,最后得到了一棵以k2为根的平衡二叉树树。

双旋转代码:左右旋转

 private BinaryNodeInterface<T> rotateLeftRight(BinaryNodeInterface<T> nodeN){
         BinaryNodeInterface<T> nodeL = nodeN.getLeftChild();
         nodeN.setLeftChild(rotateLeft(nodeL));
         return rotateRight(nodeN);
     }    

说了那么多,是不是已经有点晕了?
下面我来总结一下:
LL右旋转,RR左旋转,LR左右旋,RL右左旋。OVER!

A的左孩子的左子树插入节点(LL)

在这里插入图片描述节点A的左孩子为B,B的左子树为D,无论在节点D的左子树或者右子树中插入F均会导致节点A失衡。因此需要对节点A进行旋转操作。A的平衡因子为2,值为正,因此对A进行右旋操作。

操作流程:
在这里插入图片描述在这里插入图片描述

A的左孩子的右子树插入节点(LR)

若A的左孩子节点B的右子树E插入节点F,导致节点A失衡,如图:
在这里插入图片描述这种插入方式需要执行两步操作:

(1)对失衡节点A的左孩子B进行左旋操作,即RR情形操作。
(2)对失衡节点A做右旋操作,即LL情形操作。

在这里插入图片描述在这里插入图片描述

删除

平衡二叉树的删除情况与二叉搜索树删除情况相同,同样分为四种情况:
(1)删除叶子节点
(2)删除节点只有左子树
(3)删除节点只有右子树
(4)删除节点既有左子树又有右子树
平衡二叉树的节点删除与二叉搜索树删除方法一致,但是需要在节点删除后判断树是否仍然保持平衡,若出现失衡情况,需要进行调整。

考题部分:
LeetCode NO110平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
boolean isbalnced2(TreeNode treeNode){
        if (treeNode==null){
            return true;
        }
        if (Math.abs(findDepth(treeNode.left)-findDepth(treeNode.right))>1){
            return false;
        }
        return isbalnced2(treeNode.left)&&isbalnced2(treeNode.right);
    }
    public  int findDepth(TreeNode root){
        if (root==null){
            return 0;
        }
        int leftDepth = findDepth(root.left);
        int rightDepth = findDepth(root.right);
        return Math.max(leftDepth,rightDepth)+1;
    }
LeetCode NO.111 二叉树的最小深度
 public int minDepth(TreeNode root) {
        if (root == null) return 0;
        if (!(root.left==null && root.right==null)){
            if (root.left==null || root.right==null){
                if (root.left==null){
                    return minDepth(root.right)+1;
                }
                else return  minDepth(root.left)+1;
            }
        }


        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        return Math.min(leftDepth,rightDepth)+1;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值