平衡树(AVL树)java代码

debug了好久才写出来的平衡树,代码里有注释可以方便理解
可能有代码冗余或者有误的地方,欢迎指出错误
(好像因为是看了别人的博客,所以混淆了结点高度这个概念,我先解释一下我代码里的Height=当前结点到叶子结点的经过的边+1)
很抱歉影响了你的观看体验!
在这里插入图片描述
在这里插入图片描述

推荐一个网站(数据结构操作可视化)
(我这里给出的是平衡树界面)
AVL Tree Visualzation
(我debug就是看着可视化的树查错的)

还是讲下一些关键操作吧:
1、左旋(基操)
左旋不会改变中序遍历
在这里插入图片描述
2、右旋(基操)
右旋不会改变中序遍历
在这里插入图片描述

3、LL(左左)
用下图举例的话,就是插入操作为在结点1的左孩子的左孩子处插入结点3,这样会使得平衡度== 2
就是对结点1进行一个右旋
在这里插入图片描述
4、RR(右右,在结点1的右孩子的右孩子处插入结点3,这样会使得平衡度==-2)
就是对结点1进行一个左旋
在这里插入图片描述
5、LR(左右,在结点1的左孩子的右孩子处插入结点3,这样会使得平衡度==-2)
就是先对结点2右旋,再对结点1左旋
在这里插入图片描述
6、RL(开摆!你们自己对照着LR比划去罢)
在这里插入图片描述

7、计算平衡度
首先得到当前结点的左右孩子的高度(高度的更新是在回溯的时候进行的,所以可以保证子结点的高度已经是正确的了)
然后就是return leftHeight-rightHeight了

8、找到左子树的最大值
删除一个左右子结点都存在的结点时,需要用左子树的最大结点或右子树的最小结点来替代当前结点(我的代码里用的是左子树的最大结点)
其实左子树的最大结点就是左子树的最右下角的结点

下面开始放代码(欢迎喷我代码的问题)
在这里插入图片描述

import java.util.LinkedList;

/**
 * @author 20级三班刘宇阳
 * @create 2022/2/3
 */

/**
 * 平衡樹
 * @param <K> key的类型 元素必须可比较
 * @param <V> value的类型
 */
public class AVLTree<K extends Comparable<K>,V> {
    //树结点类
    private class Node{
        //键
        private K key;
        //值
        private V value;
        //高度
        private Integer height;
        //左右父
        private Node left,right,parent;

        public Node(K key,V value) {
            this.key=key;
            this.value = value;
            height=1;
        }

        /**
         * 获得平衡度
         * @return 平衡度
         */
        private int getBalanceFactor(){
            int leftHeight=left==null?0:left.getHeight();
            int rightHeight=right==null?0:right.getHeight();
            return leftHeight-rightHeight;
        }

        private int getHeight(){
            return height;
        }

        private void setHeight(Integer height){
            this.height=height;
        }

        /**
         * 更新该结点的高度
         */
        private void updateHeight(){
            int leftHeight=left==null?0:left.getHeight();
            int rightHeight=right==null?0:right.getHeight();
            height=1+Math.max(leftHeight,rightHeight);
        }

        private K getKey() {
            return key;
        }

        private void setKey(K key) {
            this.key = key;
        }

        private V getValue() {
            return value;
        }

        private void setValue(V value) {
            this.value = value;
        }

        private void setKeyAndValue(K key,V value){
            setKey(key);
            setValue(value);
        }

        private Node getLeft() { return left; }

        private void setLeft(Node left) { this.left = left; }

        private Node getRight() { return right; }

        private void setRight(Node right) { this.right = right; }

        private Node getParent() { return parent; }

        private void setParent(Node parent) { this.parent = parent; }

        @Override
        public String toString() {
            return "Node{" +
                    "key=" + key +
                    ", value=" + value +
                    '}';
        }
    }
    //结点个数
    private int size;
    //根结点
    private Node root;

    public AVLTree(){
        clear();
    }

    /**
     *    1              2
     *     \            / \
     *      2     =>   1   3
     *       \
     *        3
     * @param node1 需要调整的该子树的根结点
     * @return 新的根结点
     */
    private Node leftRotate(Node node1){
        Node node2=node1.getRight();
        node1.setRight(node2.getLeft());
        node1.setParent(node2);
        if(node1.getRight()!=null){
            node1.getRight().setParent(node1);
        }
        node2.setLeft(node1);
        node1.updateHeight();
        node2.updateHeight();
        return node2;
    }

    /**
     *      1       2
     *     /  =>   / \
     *    2       3   1
     *   /
     *  3
     * @param node1 需要调整的该子树的根结点
     * @return 新的根结点
     */
    private Node rightRotate(Node node1){
        Node node2=node1.getLeft();
        node1.setLeft(node2.getRight());
        node1.setParent(node2);
        if(node1.getLeft()!=null){
            node1.getLeft().setParent(node1);
        }
        node2.setRight(node1);
        node1.updateHeight();
        node2.updateHeight();
        return node2;
    }

    /**
     * 作为put的递归工具方法
     * @param root 当前的根结点
     * @param key
     * @param value
     * @return
     */
    private Node addNode(Node root,K key,V value){
        if(root==null){
            //直接返回结点,让父节点更新子结点,同时不走下面的reBalance
            return new Node(key,value);
        }
        int cmpRes=key.compareTo(root.getKey());
        //小于,看左子树
        if(cmpRes<0){
            //这里需要更新左子结点
            Node leftChild=addNode(root.getLeft(),key,value);
            root.setLeft(leftChild);
            leftChild.setParent(root);
        }else if(cmpRes>0){//大于,看右子树
            //这里需要更新右子结点
            Node rightChild=addNode(root.getRight(),key,value);
            root.setRight(rightChild);
            rightChild.setParent(root);
        }else{
            //key值相等就认为是更新
            root.setValue(value);
        }
        return reBalance(root);
    }

    private Node reBalance(Node root){
        int bf=root.getBalanceFactor();
        //左子树高
        if(bf>1){
            Node leftChild=root.getLeft();
            int childBF=leftChild.getBalanceFactor();
            /**LL型
             *        1              2
             *       /              / \
             *      2              3   1
             *     /        =>
             *    3
             **/
            if(childBF>0){
                //右旋
                root=rightRotate(root);
            }
            /** LR型
             *
             *       1          1        3
             *      /          /        / \
             *     2    =>    3   =>   2   1
             *      \        /
             *       3      2
             */
            else{
                //先左旋再右旋
                //注意左旋是以2为根结点
                root.setLeft(leftRotate(root.getLeft()));
                root=rightRotate(root);
            }
        }else if(bf<-1){//右子树高
            Node rightChild=root.getRight();
            int childBF=rightChild.getBalanceFactor();
            /**RL型
             *
             *    1         1             3
             *     \         \           / \
             *      2  =>     3   =>    1   2
             *     /           \
             *    3             2
             */
            if(childBF>0){
                //注意右旋是以2为根结点
                root.setRight(rightRotate(root.getRight()));
                root=leftRotate(root);
            }
            /**RR型
             *     1              2
             *      \            / \
             *       2    =>    1   3
             *        \
             *         3
             */
            else{
                //左旋
                root=leftRotate(root);
            }
        }
        //这里可以在回溯时更新路径上的所有结点的高度(已平衡的结点会直接来这里更新高度)
        root.updateHeight();
        return root;
    }

    /**
     * 根据key值寻找结点
     * @param root 当前根节点
     * @param key 当前键
     * @return 目标结点
     */
    private Node findNode(Node root,K key){
        if(root==null){
            return null;
        }
        int cmpRes=key.compareTo(root.getKey());
        if(cmpRes<0){
            return findNode(root.getLeft(),key);
        }else if(cmpRes>0){
            return findNode(root.getRight(),key);
        }else{
            return root;
        }
    }

    /**
     * 查找要被删除结点的左子树的最大节点
     * @param root 当前根节点
     * @param deletedNode 要被删除的结点
     * @return
     */
    private Node replaceNode(Node root,Node deletedNode){
        if(root.getRight()==null){
            deletedNode.setKeyAndValue(root.getKey(),root.getValue());
            return null;
        }
        root.setRight(replaceNode(root.getRight(),deletedNode));
        root.updateHeight();
        return reBalance(root);
    }

    /**
     * 删除结点
     * @param root 当前根节点
     * @param key 目标键
     * @return 当前子树的新根节点(可能会更新也可能不会),用来给父节点更新子树
     */
    private Node deleteNode(Node root,K key){
        if(root==null){
            return null;
        }
        int cmpRes=key.compareTo(root.getKey());
        if(cmpRes<0){
            //找左子树
            Node leftChild=deleteNode(root.getLeft(), key);
            root.setLeft(leftChild);
            if(leftChild!=null){
                //
                leftChild.setParent(root);
            }
        }else if(cmpRes>0) {
            //找右子树
            Node rightChild=deleteNode(root.getRight(), key);
            root.setRight(rightChild);
            if(rightChild!=null){
                rightChild.setParent(root);
            }
        }else{
            //找到要删除的结点了
            if(root.getLeft()==null&&root.getRight()==null){//叶子结点
                if(root.getParent()==null){
                    setRoot(null);
                }
                return null;
            }else if(root.getLeft()==null){//左子树为空,右子树不为空
                root.getRight().setParent(root.getParent());
                if(root.getParent()==null){
                    setRoot(root.getRight());
                }
                return root.getRight();
            } else if(root.getRight()==null){//右子树为空,左子树不为空
                root.getLeft().setParent(root.getParent());
                if(root.getParent()==null){
                    setRoot(root.getLeft());
                }
                return root.getLeft();
            }
            else{//有两个儿子结点
                Node leftChild=root.getLeft();
                if(leftChild.getRight()==null){
                    //要被删除的结点用左儿子结点替换,同时他的右儿子要作为他左儿子的右儿子
                    leftChild.right=root.getRight();
                    root=leftChild;
                }else{
                    //用左子树的最大节点覆盖被删除的结点(可视为删除),并更新左孩子
                    Node newLeft=replaceNode(leftChild,root);
                    root.setLeft(newLeft);
                    if(newLeft!=null){
                        newLeft.setParent(root);
                    }
                }
            }
        }
        root.updateHeight();
        return reBalance(root);
    }

    /**
     * 放入元素
     * @param key 键
     * @param value 值
     */
    public void put(K key,V value){
        if(root==null){ setRoot(new Node(key,value)); }
        else{
            setRoot(addNode(root,key,value));
            //因为这些操作不涉及根结点的处理,所以需要单独写一个根结点的父节点置空
            getRoot().setParent(null);
        }
        size++;
    }

    /**
     * 获取键对应的值
     * @param key
     * @return 对应结点的值
     */
    public V get(K key){
        if(key==null){
            throw new NullPointerException("key cannot be null");
        }
        Node node=findNode(root,key);
        if(node==null){
            return null;
        }
        return node.getValue();
    }

    /**
     * 根据键删除对应结点
     * @param key 键
     */
    public void remove(K key){
        if(key==null){
            throw new NullPointerException("key cannot be null");
        }
        setRoot(deleteNode(root,key));
        if(getRoot()!=null){
            getRoot().setParent(null);
        }
        size--;
    }

    /**
     * 重置树(可用于初始化)
     */
    public void clear(){
        size=0;
        root=null;
    }

    public boolean isEmpty(){ return size==0; }

    public int size() { return size; }

    private void setSize(int size) { this.size = size; }

    private Node getRoot() { return root; }

    private void setRoot(Node root) { this.root = root; }

    private StringBuilder preOrderTraverse(Node root,StringBuilder sb){
        if(root==null){
            //null结点为#
            sb.append("# ");
            return sb;
        }
        sb.append(root.getKey()).append(" ");
        sb=preOrderTraverse(root.getLeft(),sb);
        sb=preOrderTraverse(root.getRight(),sb);
        return sb;
    }

    @Override
    public String toString(){
        StringBuilder sb=new StringBuilder();
        sb.append("preOrderTraverseSequenceOfKey is [");
        sb=preOrderTraverse(root,sb);
        sb.deleteCharAt(sb.length()-1);
        sb.append("]");
        return sb.toString();
    }
}

class AVLTreeDemo{
    public static void main(String[] args) {
        AVLTree<Integer,Integer> tree=new AVLTree<>();
        LinkedList<Integer> list=new LinkedList<>();
        for(int i=1;i<=10;i++){
            list.add(i);
        }
        list.add(0);
//        System.out.println(list);
        System.out.println("----------put测试----------");
        System.out.println("输出的是前序遍历的key序列:");
        for(Integer it:list){
            tree.put(it,it);
            System.out.println(tree);
        }
        tree.remove(2);
        System.out.println(tree);
//        System.out.println("------------get测试--------------");
//        System.out.println(tree.get(2));
//        System.out.println("------------delete测试--------------");
//        tree.remove(2);
//        System.out.println(tree);
//        tree.remove(1);
//        System.out.println(tree);
//        tree.remove(-1);
//        System.out.println(tree);
//        tree.remove(0);
//        System.out.println(tree);
//        tree.put(11,11);
//        System.out.println(tree);
//        tree.remove(8);
//        System.out.println(tree);
//        tree.remove(9);
//        tree.remove(11);
//        System.out.println(tree);
//        tree.remove(3);
//        System.out.println(tree);
//        tree.remove(6);
//        tree.remove(4);
//        System.out.println(tree);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值