java实现平衡二叉搜索树

概述

平衡二叉搜索树,实际上就是由于二叉搜索树的左右节点的层数相差一层,导致遍历查找的时候的时间复杂度左右两侧不平衡导致的.

所以可以使用旋转法:如果一个节点的左右孩子,高度差超过1,则此节点失衡,故而需要旋转

由上可以看出左右节点失衡,故而旋转成为下图

新增或者删除的时候可能会导致不平衡,故而此时就需要判断是否需要旋转了

本质就是:如果二叉搜索树的左右节点,高度差超过1的时候,此节点失衡,此时需要通过旋转的方式将二叉搜索树变为平衡二叉搜索树

AVL树

  • 二叉搜索树在插入和删除时,节点可能会失去平衡
  • 如果在插入和删除时通过旋转,始终让二叉搜索树保持平衡,称为自平衡的二叉搜索树
  • AVL是自平衡二叉搜索树的实现之一

平衡二叉搜索树的节点类

//平衡二叉搜索树的节点类
static class AVLNode{
        int key;  //key值用来判断大小
        Object value;  //保存值元素
        AVLNode left;  //左节点
        AVLNode right; //右节点
        int height = 1; //高度

        public AVLNode(int key) {
            this.key = key;
        }

        public AVLNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public AVLNode(int key, Object value, AVLNode left, AVLNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

求节点的高度


    /**
     * 求节点高度
     * @param node
     * @return
     */
    private int height(AVLNode node){
        //用来判断传过来的节点是否为null,如果为null的话此时的高度返回0,否则返回节点高度
        return node == null ? 0 : node.height;
    }

更新节点高度

 /**
     * 更新节点高度(新增,删除,旋转)
     * @param node
     */
    private void updateHeight(AVLNode node){
        //节点的高度就等于左右节点找一个深度更深的节点,让其加1就是此时节点的新高度了
       node.height = Integer.max(height(node.left),height(node.right))+1;
    }

判断一个节点是否平衡

/**
     * 平衡因子(balance factor) = 左子树高度 - 右子树高度
     * @param node
     * @return
     */
    private int bf(AVLNode node){
        //如果结果为[-1,1]之间的话,平衡,反之不平衡
        return height(node.left)-height(node.right);
    }

四种失衡条件

条件1

此时的失衡节点是5,失衡节点的左孩子是3,失衡节点的左孩子的节点左边更高

条件2

此时失衡节点就是6,失衡节点的左孩子是2,时候节点的左右不平衡右边更高一些

条件3

此时失衡节点是2,失衡节点的右节点是6,此时失衡节点的右孩子的左边比右边更大

条件4

失衡节点是2,失衡节点的右孩子是4,失衡节点的右孩子的节点右边比左边高

由上图四种情况可以得知

 

条件1:可以通过右旋进行改变失衡

条件2:可以先通过左旋,再通过右旋来改变失衡

条件3:可以先通过右旋,再通过左旋来改变失衡

条件4:可以通过左旋进行改变失衡

旋转

左旋----解决条件四

/**
     * 通过右旋实现
     * @param red  需要旋转的节点
     * @return  yellow  返回旋转过的节点
     */
    private AVLNode rightRotate(AVLNode red){
        AVLNode yellow = red.left; //找到红色节点
        AVLNode green = yellow.right; //找到绿色节点
        yellow.right = red; //将黄色节点的右侧变为红色节点
        red.left = green;   //将红色节点的左侧变为绿色节点
        updateHeight(red);  //更新红色节点的高度
        updateHeight(yellow); //更新黄色节点的高度
        return yellow;    //最终返回旋转之后的黄色节点
    }

右旋----解决条件一

/**
     * 通过左旋实现
     * @param red 需要旋转的节点
     * @return   yellow  返回旋转过的节点
     */
    private AVLNode leftRotate(AVLNode red){
        AVLNode yellow = red.right; //找到黄色节点
        AVLNode green = yellow.left; //找到绿色节点
        yellow.left = red; //黄色节点的左侧换位红色节点
        red.right = green; //红色节点的右侧换位绿色节点
        updateHeight(red);  //更新红色节点的高度
        updateHeight(yellow); //更新黄色节点的高度
        return yellow;
    }
}

先左旋再右旋---解决条件二

node.left = leftRotate(node.left);  //根节点的左孩子等于旋转之后

rightRotate(node);

/**
     * 先左旋左子树,再右旋根节点
     * @param node
     * @return
     */
    private AVLNode leftRightRotate(AVLNode node){
       node.left = leftRotate(node.left);  //根节点的左孩子等于旋转之后
        return rightRotate(node);  //再次右旋根节点
    }

先右旋再左旋---解决条件三

 /**
     * 先右旋右子树,再左旋根节点
     * @param node
     * @return
     */
    private AVLNode rightLeftRotate(AVLNode node){
        node.right =  rightRotate(node.right); //先右旋右子树
        return leftRotate(node);  //再次左旋根节点
    }

失衡解决

/**
     * 检查节点是否失衡,重新平衡代码
     * @param node
     * @return
     */
    private AVLNode balance(AVLNode node){
        if(node == null){
            return null;
        }
        int bf = bf(node);
        if(bf > 1 && bf(node.left) >= 0){  //条件1
           return rightRotate(node);
        }else if(bf > 1 && bf(node.left) < 0){   //条件3
           return leftRightRotate(node);
        }else if(bf < -1 && bf(node.right) > 0){  //条件4
           return rightLeftRotate(node);
        }else if(bf < -1 && bf(node.right) <= 0 ){  //条件2
            return leftRotate(node);
        }
        return node;
    }

新增

AVLNode root;
    public void put(int key,Object value){
       root = dePut1(root,key,value);
    }

    /**
     * 非递归
     * @param node
     * @param key
     * @param value
     * @return
     */
    private AVLNode dePut(AVLNode node,int key,Object value){
        //找到空位创建新节点
        //key在树中已经存在,更新
        //继续查找
        // 将当前节点初始化为AVL树的根节点
        AVLNode curr = node;
        // 将父节点初始化为null,用于跟踪当前节点的父节点
        AVLNode parent = null;
        // 遍历AVL树,直到当前节点为null
        while (curr != null){
            // 如果给定的键小于当前节点的键
            if(key < curr.key){
                // 更新父节点并移动到左子节点
                parent = curr;
                curr = curr.left;
            }
            // 如果给定的键大于当前节点的键
            else if(key > curr.key){
                // 更新父节点并移动到右子节点
                parent = curr;
                curr = curr.right;
            }
            // 如果给定的键与当前节点的键相匹配
            else{
                // 更新现有节点的值并返回该节点
                curr.value = value;
                return curr;
            }
        }
        // 如果树为空(没有根节点),返回null
        if(parent == null){
            return null;
        }
        // 根据新节点与其父节点的关系,插入具有给定键和值的新节点
        if(key < parent.key){
            // 如果键小于父节点的键,则插入为左子节点
            parent.left = new AVLNode(key, value);
        }
        else if(key > parent.key){
            // 如果键大于父节点的键,则插入为右子节点
            parent.right = new AVLNode(key, value);
        }
        updateHeight(curr); //更新节点高度
        return balance(curr); //重新平衡代码
    }

    /**
     * 递归方式
     * @param node
     * @param key
     * @param value
     * @return
     */
    private AVLNode dePut1(AVLNode node,int key,Object value){
        //找到空位创建新节点
        //key在树中已经存在,更新
        //继续查找
        // 如果当前节点为空,表示在空树中插入新节点,返回新的AVL节点
        if(node == null){
            return new AVLNode(key, value);
        }
        // 如果给定的键等于当前节点的键,更新当前节点的值并返回当前节点
        if(key == node.key){
            node.value = value;
            return node;
        }
        // 如果给定的键小于当前节点的键,递归地在左子树中插入新节点
        if(key < node.key){
            node.left = dePut1(node.left, key, value);
        }
        // 如果给定的键大于当前节点的键,递归地在右子树中插入新节点
        else{
            node.right = dePut1(node.right, key, value);
        }
        updateHeight(node); //更新节点的高度
        return balance(node); //重新平衡
    }

删除

private AVLNode doRemove(AVLNode node,int key){
        //1.node == null
       if(node == null){
           return null;
       }
        //2.没找到key
       if(key < node.key){
          node.left = doRemove(node.left,key);
       }else if(key > node.key){
           node.right = doRemove(node.right,key);
       }else{
           //3.找到key
           //左右孩子都为空
           if(node.left == null && node.right == null){
                return null;
           }
           //判断只有右孩子
           else if(node.left == null){
                node = node.right;
           }
           //判断只有左孩子
           else if(node.right == null){
                node = node.left;
           }
           //判断左右孩子都存在
           else{
                AVLNode s = node.right;
                while (s.left != null){
                    s = s.left;
                }
                //找到后继节点
              s.right = doRemove(node.right,s.key);
                s.left = node.left;
                node = s;
           }
       }
        //4.更新高度
       updateHeight(node);
        //5.检查失衡
       return balance(node);
   }

总代码

public class AVLTree {

    static class AVLNode{
        int key;  //key值用来判断大小
        Object value;  //保存值元素
        AVLNode left;  //左节点
        AVLNode right; //右节点
        int height = 1; //高度

        public AVLNode(int key) {
            this.key = key;
        }

        public AVLNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public AVLNode(int key, Object value, AVLNode left, AVLNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    /**
     * 求节点高度
     * @param node
     * @return
     */
    private int height(AVLNode node){
        return node == null ? 0 : node.height;
    }

    /**
     * 更新节点高度(新增,删除,旋转)
     * @param node
     */
    private void updateHeight(AVLNode node){
       node.height = Integer.max(height(node.left),height(node.right))+1;
    }

    /**
     * 平衡因子(balance factor) = 左子树高度 - 右子树高度
     * @param node
     * @return
     */
    private int bf(AVLNode node){
        return height(node.left)-height(node.right);
    }

    /**
     * 通过右旋实现
     * @param red  需要旋转的节点
     * @return  yellow  返回旋转过的节点
     */
    private AVLNode rightRotate(AVLNode red){
        AVLNode yellow = red.left; //找到旋转的节点
        AVLNode green = yellow.right; //找到旋转节点的右节点
        yellow.right = red; //旋转之后的右节点是旋转之前的节点
        red.left = green;   //旋转子厚的右节点的左节点是旋转节点的右节点
        updateHeight(red);  //更新红色节点的高度
        updateHeight(yellow); //更新黄色节点的高度
        return yellow;
    }

    /**
     * 通过左旋实现
     * @param red 需要旋转的节点
     * @return   yellow  返回旋转过的节点
     */
    private AVLNode leftRotate(AVLNode red){
        AVLNode yellow = red.right; //找到黄色节点
        AVLNode green = yellow.left; //找到绿色节点
        yellow.left = red; //黄色节点的左侧换位红色节点
        red.right = green; //红色节点的右侧换位绿色节点
        updateHeight(red);  //更新红色节点的高度
        updateHeight(yellow); //更新黄色节点的高度
        return yellow;
    }

    /**
     * 先左旋左子树,再右旋根节点
     * @param node
     * @return
     */
    private AVLNode leftRightRotate(AVLNode node){
       node.left = leftRotate(node.left);  //根节点的左孩子等于旋转之后
        return rightRotate(node);  //再次右旋根节点
    }

    /**
     * 先右旋右子树,再左旋根节点
     * @param node
     * @return
     */
    private AVLNode rightLeftRotate(AVLNode node){
        node.right =  rightRotate(node.right); //先右旋右子树
        return leftRotate(node);  //再次左旋根节点
    }

    /**
     * 检查节点是否失衡,重新平衡代码
     * @param node
     * @return
     */
    private AVLNode balance(AVLNode node){
        if(node == null){
            return null;
        }
        int bf = bf(node);
        if(bf > 1 && bf(node.left) >= 0){  //条件1
           return rightRotate(node);
        }else if(bf > 1 && bf(node.left) < 0){   //条件3
           return leftRightRotate(node);
        }else if(bf < -1 && bf(node.right) > 0){  //条件4
           return rightLeftRotate(node);
        }else if(bf < -1 && bf(node.right) <= 0 ){  //条件2
            return leftRotate(node);
        }
        return node;
    }

    AVLNode root;
    public void put(int key,Object value){
       root = dePut1(root,key,value);
    }

    /**
     * 非递归
     * @param node
     * @param key
     * @param value
     * @return
     */
    private AVLNode dePut(AVLNode node,int key,Object value){
        //找到空位创建新节点
        //key在树中已经存在,更新
        //继续查找
        // 将当前节点初始化为AVL树的根节点
        AVLNode curr = node;
        // 将父节点初始化为null,用于跟踪当前节点的父节点
        AVLNode parent = null;
        // 遍历AVL树,直到当前节点为null
        while (curr != null){
            // 如果给定的键小于当前节点的键
            if(key < curr.key){
                // 更新父节点并移动到左子节点
                parent = curr;
                curr = curr.left;
            }
            // 如果给定的键大于当前节点的键
            else if(key > curr.key){
                // 更新父节点并移动到右子节点
                parent = curr;
                curr = curr.right;
            }
            // 如果给定的键与当前节点的键相匹配
            else{
                // 更新现有节点的值并返回该节点
                curr.value = value;
                return curr;
            }
        }
        // 如果树为空(没有根节点),返回null
        if(parent == null){
            return null;
        }
        // 根据新节点与其父节点的关系,插入具有给定键和值的新节点
        if(key < parent.key){
            // 如果键小于父节点的键,则插入为左子节点
            parent.left = new AVLNode(key, value);
        }
        else if(key > parent.key){
            // 如果键大于父节点的键,则插入为右子节点
            parent.right = new AVLNode(key, value);
        }
        updateHeight(curr); //更新节点高度
        return balance(curr); //重新平衡代码
    }

    /**
     * 递归方式
     * @param node
     * @param key
     * @param value
     * @return
     */
    private AVLNode dePut1(AVLNode node,int key,Object value){
        //找到空位创建新节点
        //key在树中已经存在,更新
        //继续查找
        // 如果当前节点为空,表示在空树中插入新节点,返回新的AVL节点
        if(node == null){
            return new AVLNode(key, value);
        }
        // 如果给定的键等于当前节点的键,更新当前节点的值并返回当前节点
        if(key == node.key){
            node.value = value;
            return node;
        }
        // 如果给定的键小于当前节点的键,递归地在左子树中插入新节点
        if(key < node.key){
            node.left = dePut1(node.left, key, value);
        }
        // 如果给定的键大于当前节点的键,递归地在右子树中插入新节点
        else{
            node.right = dePut1(node.right, key, value);
        }
        updateHeight(node); //更新节点的高度
        return balance(node); //重新平衡
    }

   public void remove(int key){
       root = doRemove(root,key);
   }

   private AVLNode doRemove(AVLNode node,int key){
        //1.node == null
       if(node == null){
           return null;
       }
        //2.没找到key
       if(key < node.key){
          node.left = doRemove(node.left,key);
       }else if(key > node.key){
           node.right = doRemove(node.right,key);
       }else{
           //3.找到key
           //左右孩子都为空
           if(node.left == null && node.right == null){
                return null;
           }
           //判断只有右孩子
           else if(node.left == null){
                node = node.right;
           }
           //判断只有左孩子
           else if(node.right == null){
                node = node.left;
           }
           //判断左右孩子都存在
           else{
                AVLNode s = node.right;
                while (s.left != null){
                    s = s.left;
                }
                //找到后继节点
              s.right = doRemove(node.right,s.key);
                s.left = node.left;
                node = s;
           }
       }
        //4.更新高度
       updateHeight(node);
        //5.检查失衡
       return balance(node);
   }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值