AVLTree

平衡二叉树

二叉查找树的问题

当我们依次添加n个有序键值对的时候,二叉查找树的高度为n,这样就大大降低了查找的效率,因为每次查找还要判断每个结点中一个空的链接,甚至比单链表还要慢,为了解决这个问题,我们希望构造的二叉查找树的高度为~lgN,这就是我们要说的平衡二叉树。

简单介绍

1)平衡二叉树也叫做平衡二叉查找树(Self-balancing binary search tree)又称为AVL树,可以保证查询效率高。
2)具有以下特点:它的左右两个子树的高度差绝对值不超过1,并且两个子树都是平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。

构建平衡二叉树

要构造平衡二叉树,首先我们需要知道当前节点左右子树的深度,先构造个方法返回左右子树高度。

    //返回左子树的高度
    public int leftHeight(TreeNode root){
        return height(root.left);
    }
    public int leftHeight(){
        return height(root.left);
    }
    //返回右子树的高度
    private int rightHeight(TreeNode root){
        return height(root.right);
    }
    public int rightHeight(){
        return height(root.right);
    }
    //返回树的高度
    private int height(TreeNode root){
        if(root == null) return 0;
        return Math.max(height(root.left) + 1,height(root.right) + 1);
    }
    public int height(){
        if(root == null) return 0;
        return Math.max(height(root.left) + 1,height(root.right) + 1);
    }

知道了高度以后,就是对树结构的操作了,这里介绍一下左右旋转。

单旋转

左旋转

ps:以下说的根结点指要进行旋转的树或子树的根结点

  1. 创建一个新的结点,它的值为根结点的值
  2. 将新节点的左连接指向根结点的左结点
  3. 将新节点的右连接指向根结点的右结点的左节点
  4. 把根结点的值换位根结点的右子结点的值
  5. 根结点的左链接指向新节点
  6. 根结点的右链接指向根结点右子节点的右子节点
右旋转
  1. 创建一个新的结点,它的值为根结点的值
  2. 将新节点的右连接指向根结点的右结点
  3. 将新节点的左连接指向根结点的左结点的右节点
  4. 把根结点的值换位根结点的左子结点的值
  5. 根结点的右链接指向新节点
  6. 根结点的左链接指向根结点左子节点的左子节点

当树的左子树高度 - 右子树高度 > 1时,就需要进行右旋转,相反,如果右子树高度 - 左子树高度 > 1,就要进行左旋转。
例如:
在这里插入图片描述
在这里插入图片描述

但是目前的旋转还有一个问题,例如:给定int[] key{7,6,10,8,11,9},这样进行左旋转以后还是不平衡,所以我们少考虑了其他情况。

双旋转

左旋转

设right为要进行旋转的根结点的右子结点,如果right的左子树比右子树高度高时,我们需要先对right进行一次右旋转,再对根结点进行左旋转。

右旋转

设left为要进行右旋转的左子结点,如果left的右子树比左子树高度高,我们需要先队left进行左旋转,再对根结点进行右旋转。

代码

根据以上的分析,我们代码实现对平衡二叉树的构造
首先我们创建单旋转的两个方法。

    public void leftRotate(TreeNode root){
        TreeNode newNode = new TreeNode(root.key,root.val);
        newNode.left = root.left;
        newNode.right = root.right.left;
        root.key = root.right.key;
        root.val = root.right.val;
        root.left = newNode;
        root.right = root.right.right;
    }
    //右旋转
    public void rightRotate(TreeNode root){
        TreeNode newNode = new TreeNode(root.key,root.val);
        newNode.right = root.right;
        newNode.left = root.left.right;
        root.key = root.left.key;
        root.val = root.left.val;
        root.right = newNode;
        root.left = root.left.left;
    }

其次构造一个调整二叉查找树的方法

    //调整二叉查找树
    public void reCreate(TreeNode root){
        if((rightHeight(root) - leftHeight(root)) > 1){//右子树高度比左子树高度大1以上
            if(leftHeight(root.right) > rightHeight(root.right)){//如果右结点的左子树比右结点的右子树高度大,先对右结点右旋转
                rightRotate(root.right);
            }
            leftRotate(root);
        }else if((leftHeight(root) - rightHeight(root)) > 1){//左子树高度比右子树高度大1以上
            if(rightHeight(root.left) > leftHeight(root.left)){
                leftRotate(root.left);
            }
            rightRotate(root);
        }
    }

当我们为二叉树添加节点时,在返回新的根结点之前我们对二叉查找树进行一次调整,二叉查找树的添加操作之前我们已经说过,这里之间拿之前的代码在返回值之前调用一些reCreate就行了。

    //搜索树添加结点方法
    public void put(Key key,Value val){
        root = put(root,key,val);
    }
    private TreeNode put(TreeNode root, Key key, Value val){
        //如果key存在于以root为根结点的树中,则更新value值,否则将key value插入到树中
        if(root == null) return new TreeNode(key,val);
        if(root.key.compareTo(key) > 0) root.left = put(root.left,key,val);
        else if(root.key.compareTo(key) < 0) root.right = put(root.right,key,val);
        else root.val = val;
        reCreate(root);
        return root;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值