平衡二叉树(AVLTree)

1.平衡二叉树的定义

1.1 什么是平衡二叉树

平衡二叉树,又称AVL树,用于解决二叉排序树高度不确定的情况,如果二叉排序树的子树间的高度相差太大,就会让二叉排序树操作的时间复杂度升级为O(n),为了避免这一情况,为最坏的情况做准备,就出现了平衡二叉树,使树的高度尽可能的小,其本质还是一棵二叉搜索树。

 1.2 平衡二叉树的性质

  1. 左子树和右子树的高度之差的绝对值小于等于1
  2. 左子树和右子树也是平衡二叉树

为了方便起见,给树上的每个结点附加一个数字,给出该结点左子树与右子树的高度差,这个数字称为结点的平衡因子(BF)

                平衡因子 = 右子树的高度 - 左子树的高度

  左右子树的高度差(平衡因子)< 2,因此平衡二叉树所有结点的平衡因子只能是-1、0、1,如下图,是一个平衡二叉树

 

2.平衡二叉树的基本代码实现

2.1平衡二叉树的插入

首先我们创建一颗二叉树,在开发软件中创建一个AVLTree类,增添树的特性。

 static class TreeNode{
        public int bf;
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;
        public TreeNode(int val){
            this.val = val;
        }
    }
    public TreeNode root;

接下来我们可以插入一个节点,创建一个boolean类型的insert方法同时传入int val。插入一个树首先考虑插入的树是否为空,如果为空直接添加,如果不为空,就需要比较该节点与插入节的大小。由AVLTree的性质可以知道如果插入的树大于该节点就往改节点的右边走,如果插入的树小于该节点就往改节点的左边走,直到节点为空,以下为该段话语代码的实现:

 TreeNode node = new TreeNode(val);
        if(root == null){
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null){
            if(cur.val < val){
                parent = cur;
                cur = cur.right;
            }else if (cur.val == val){
               return false;
            }else{
                parent = cur;
                cur = cur.left;
            }
        }

 接下来cur为null,我们需要进行插入,如果父亲节点小于该节点则插入在父亲节点的右侧,如果父亲节点大于该节点则插入在父亲节点的左侧。

 //cur == null
        if(parent.val < val){
            parent.right = node;
        }else{
            parent.left = node;
        }

插入之后我们首先想到的便是平衡因子的修改。先看插入的树是处在父亲节点的左还是右,左进行减减右进行加加。平衡因子修改之后我们还需判断平衡因子是否为这三个符合条件的数字 1 ,-1 ,0  如果是0则说明已经平衡了直接break,如果是 1 或者  -1 则需要向上继续检查是否有不符合标准的情况直到parent为空,此条件便是循环条件。如果平衡因子是2 或者 -2 的话就需要考虑多种情况进行左旋还有右旋还有左右旋。

1. 第一种情况parent.bf = -2 cur.bf = -1 进行右旋。    parent = 60 ,subL = 30 ,subLR = 40

右旋之后  subL.right = parent , parent.left = subLR

 2.第二种情况 进行左旋 parent.bf = 2 cur.bf = 1

左旋之后 subR.left = parent , parent.right = subRL

 3.第三种情况  parent.bf = -2 , cur.bf = 1 进行先左旋再右旋,此时左旋传入的parent.left,即rotateleft(parent.left),右旋时传入的是parent,即rotate(parent)

左旋:subR.left = parent , parent.right = subRL

之后右旋: 

subL.right = parent

 4.第四种情况 parent.bf = 2 , cur.bf  = -1, bf = cur.left.bf

当bf = 1时,先右后左 rotate right(parent.right) , rotate left(parent)

右旋:

左旋:subR.left = parent

bf =  -1时 rotate right(parent.right), rotate left(parent) parent为30

代码实现: 

 public boolean insert(int val){
        TreeNode node = new TreeNode(val);
        if(root == null){
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null){
            if(cur.val < val){
                parent = cur;
                cur = cur.right;
            }else if (cur.val == val){
               return false;
            }else{
                parent = cur;
                cur = cur.left;
            }
        }
        //cur == null
        if(parent.val < val){
            parent.right = node;
        }else{
            parent.left = node;
        }
        //node.parent = parent;
        // cur = node
        //平衡因子修改
        while(parent != null){
            //先看平衡因子是左还是右决定平衡因子是++还是--
            if(cur == parent.right){
                parent.bf++;
            }else{
                //如果是左树,平衡因子--
                parent.bf--;
            }
            //检查平衡因子是否是 1 0 -1
            if(parent.bf == 0){
                //说明已经平衡了
                break;
            }else if(parent.bf == 1 || parent.bf == -1){
                //继续向上去修改平衡因子
                cur = parent;
                parent = cur.parent;
            }else{
                if(parent.bf == 2){
                    if(cur.bf == 1){
                        //左旋
                       rotateLeft(parent);
                    }else{
                        //cur.bf == -1 先右旋再左旋
                   rotateRL(parent);
                    }
                }else{
                    //parent.bf == -2 左树高,降低左树的高度
                    if(cur.bf == -1){
                        //右旋
                         rotateRight(parent);
                    }else{
                        //先左旋再右旋 cur.bf == 1
                       rotateLR(parent);
                    }

                }
                break;
            }
        }
        return true;
    }

2.2 左单旋

代码实现:

 private void rotateLeft(TreeNode parent){
         TreeNode subR = parent.right;
         TreeNode subRL = subR.left;
        parent.right = subRL;
         subR.left = parent;
         if(subRL != null) {
             subRL.parent = parent;
         }
         TreeNode pParent = parent.parent;
         parent.parent = subR;
         if(root == parent){
             root = subR;
             root.parent = null;
         }else{
             if(pParent.left == parent){
                 pParent.left = subR;
             }else {
                 pParent.right = subR;
             }
             subR.parent = pParent;
         }
         subR.bf = parent.bf = 0;
    }

 2.3 右单旋

代码实现:

 private void rotateRight(TreeNode parent){
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        subL.right = parent;
        if(subLR != null){
            subLR.parent = parent;
        }
        TreeNode pParent = parent.parent;
        parent.parent = subL;
        if(parent == root){
            root = subL;
            root.parent = null;
        }else{
            if(pParent.left == parent){
                pParent.left = subL;
            }else{
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
        subL.bf = 0;
        parent.bf = 0;
    }

 2.4 左右双旋

第一旋:对以A的左子树为根节点的树(即L后面结点,B-9-New)进行左旋

第二旋:再对以A为根节点的树(即全部结点)进行右旋;图中有错误第二旋为右旋

 private void rotateLR (TreeNode parent){
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;
        rotateLeft(parent.left);
        rotateRight(parent);
        if(bf == 1){
            subLR.bf = 0;
            parent.bf = 0;
            subL.bf = -1;
        }else if(bf == -1){
            subLR.bf = 0;
            subL.bf = 0;
            parent.bf = 1;
        }
    }

 2.5 右左双旋

第一旋:对以A的右子树为根节点的树(即R后面结点,B-13-10-New)进行右旋

第二旋:再对以A为根节点的树(即全部结点)进行左旋

代码实现:

 private void rotateRL (TreeNode parent){
      TreeNode subR = parent.right;
      TreeNode subRL = subR.left;
      int bf = subRL.bf;
      rotateRight(parent.left);
      rotateLeft(parent);
      if(bf == 1){
          subRL.bf = 0;
          parent.bf = -1;
          subR.bf = 0;
      }else if(bf == -1){
          subRL.bf =0;
          parent.bf = 0;
          subR.bf = 1;
      }
    }

3.检验是否为AVL树

 public boolean isBalanced (TreeNode root) {
       if (root == null) return true;

       int leftH = height(root.left);
       int rightH = height(root.right);

       if (rightH - leftH != root.bf) {
           System.out.println("这个因子" + root.val + "异常");
           return false;
       }
       return Math.abs(leftH - rightH) <= 1 && isBalanced(root.left) && isBalanced(root.right);
   }

 

 完整代码:

public class AVLTree {
    static class TreeNode{
        public int bf;
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;
        public TreeNode(int val){
            this.val = val;
        }
    }
    public TreeNode root;
    public boolean insert(int val){
        TreeNode node = new TreeNode(val);
        if(root == null){
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while(cur != null){
            if(cur.val < val){
                parent = cur;
                cur = cur.right;
            }else if (cur.val == val){
               return false;
            }else{
                parent = cur;
                cur = cur.left;
            }
        }
        //cur == null
        if(parent.val < val){
            parent.right = node;
        }else{
            parent.left = node;
        }
        //node.parent = parent;
        // cur = node
        //平衡因子修改
        while(parent != null){
            //先看平衡因子是左还是右决定平衡因子是++还是--
            if(cur == parent.right){
                parent.bf++;
            }else{
                //如果是左树,平衡因子--
                parent.bf--;
            }
            //检查平衡因子是否是 1 0 -1
            if(parent.bf == 0){
                //说明已经平衡了
                break;
            }else if(parent.bf == 1 || parent.bf == -1){
                //继续向上去修改平衡因子
                cur = parent;
                parent = cur.parent;
            }else{
                if(parent.bf == 2){
                    if(cur.bf == 1){
                        //左旋
                       rotateLeft(parent);
                    }else{
                        //cur.bf == -1 先右旋再左旋
                   rotateRL(parent);
                    }
                }else{
                    //parent.bf == -2 左树高,降低左树的高度
                    if(cur.bf == -1){
                        //右旋
                         rotateRight(parent);
                    }else{
                        //先左旋再右旋 cur.bf == 1
                       rotateLR(parent);
                    }

                }
                break;
            }
        }
        return true;
    }

    /**
     * 右左双旋
     * @param parent
     */
    private void rotateRL (TreeNode parent){
      TreeNode subR = parent.right;
      TreeNode subRL = subR.left;
      int bf = subRL.bf;
      rotateRight(parent.left);
      rotateLeft(parent);
      if(bf == 1){
          subRL.bf = 0;
          parent.bf = -1;
          subR.bf = 0;
      }else if(bf == -1){
          subRL.bf =0;
          parent.bf = 0;
          subR.bf = 1;
      }
    }
    /**
     * 左右双旋
     */
    private void rotateLR (TreeNode parent){
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;
        rotateLeft(parent.left);
        rotateRight(parent);
        if(bf == 1){
            subLR.bf = 0;
            parent.bf = 0;
            subL.bf = -1;
        }else if(bf == -1){
            subLR.bf = 0;
            subL.bf = 0;
            parent.bf = 1;
        }
    }

    /**
     * 左单旋
     * @param parent
     */
    private void rotateLeft(TreeNode parent){
         TreeNode subR = parent.right;
         TreeNode subRL = subR.left;
        parent.right = subRL;
         subR.left = parent;
         if(subRL != null) {
             subRL.parent = parent;
         }
         TreeNode pParent = parent.parent;
         parent.parent = subR;
         if(root == parent){
             root = subR;
             root.parent = null;
         }else{
             if(pParent.left == parent){
                 pParent.left = subR;
             }else {
                 pParent.right = subR;
             }
             subR.parent = pParent;
         }
         subR.bf = parent.bf = 0;
    }

    /**
     * 右单旋
     * @param parent
     */
    private void rotateRight(TreeNode parent){
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        subL.right = parent;
        if(subLR != null){
            subLR.parent = parent;
        }
        TreeNode pParent = parent.parent;
        parent.parent = subL;
        if(parent == root){
            root = subL;
            root.parent = null;
        }else{
            if(pParent.left == parent){
                pParent.left = subL;
            }else{
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
        subL.bf = 0;
        parent.bf = 0;
    }

  public boolean isBalanced (TreeNode root) {
       if (root == null) return true;

       int leftH = height(root.left);
       int rightH = height(root.right);

       if (rightH - leftH != root.bf) {
           System.out.println("这个因子" + root.val + "异常");
           return false;
       }
       return Math.abs(leftH - rightH) <= 1 && isBalanced(root.left) && isBalanced(root.right);
   }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值