平衡二叉树

目录

 

1.AVL树的介绍

2.平衡二叉树的实现:


1.AVL树的介绍

1.1什么是AVL树

​ avl树是平衡二叉树,是一种搜索树,所以平衡二叉树满足搜索树的性质。即平衡二叉树的左子树的所有节点的值小于该树的根节点的值,平衡二叉树的右子树大于该树的根节点的值。该性质是一种递归的定义,意味着根节点的左子树和右子树作为单独的平衡二叉树的时候也满足上述性质。同时平衡二叉树的任意节点的左子树与右子树的高度差不应当超过2.

​ 也就意味着,平衡二叉树维护了二叉树的平衡性。所以不会因为数据的原因使得该树降为一个单链表。例如当输入的数据为从小到大的有序数列时,如果没有做平衡操作,根据搜索树的性质会使该树成为一个单只树。

1.2平衡因子

​ 判断需要做平衡操作的指标:平衡因子。

​ 当节点的平衡因子的绝对值大于1时需要进行平衡操作。

​ 平衡因子计算:

​ 节点node的平衡因子=node左子树的高度-node右子树的高度

1.3平衡操作

  • LL

    如下图:LL型意味着X的节点的平衡因子大于1,并且Y的平衡因子大于0.

    所以需要将X做右旋转

    操作伪代码

    Node y=x.right;
    Node right=y.right;
    y.right=x;
    x.left=right;

     

  • LR

    如图所示:RR意味着x的节点的平衡因子大于1,并且Y节点的平衡因子小于0

    操作:先对Y节点做左旋转,然后在对X节点做右旋转

    伪代码:

    //调用左旋转方法,对Y节点进行左旋转,返回旋转后的根节点。然后连接上X节点
    x.left=leftRotate(x.left);
    //对X节点再做右旋转
    return rightRotate(x);

     

    对于RR和RL是与上面相反的操作。需要注意的是判断条件。

    对于RR的判断条件是:X的平衡因子小于-1,并且Y的平衡因子小于0

    对于RL的判断条件是:X的平衡因子小于-1,并且Y的平衡因子大于0

2.平衡二叉树的实现:

2.1 结构定义

public class AVL<K extends Comparable<K>,V> {
    //内部类,平衡二叉树的每一个节点的定义
    private class Node{
        //节点的数据域,键值对
        private K key;
        private V value;
        //节点的在平衡二叉树的高度
        private int height;
        //指向左子树和右子树的引用
        private Node right,left;
        
        //节点的构造方法
        private Node(K key,V value){
            this.key=key;
            this.value=value;
            height=1;
            right=left=null;
        }
    }
    //平衡二叉树中的节点个数
    private int size;
    //平衡二叉树的根节点
    private Node root;
}

2.2 核心方法

2.2.1得到指定节点的平衡因子

/**
     * 得到节点node的平衡因子
     * @param node
     * @return
     */
    private int getBalanceFactor(Node node) {
        if(node==null){
            return 0;
        }
        int balanceFactor =getHeight(node.left)-getHeight(node.right);
        return balanceFactor;
    }
​

2.2.2得到指定节点的高度

/**
     * 得到节点node的高度
     * @param node
     * @return
     */
    private int getHeight(Node node) {
        if(node==null){
            return 0;
        }
        return node.height;
    }

2.2.3右旋转

/**
 * 对以node为根的二叉树进行右旋转操作
 * @param node
 * @return
 * 
 */
private Node rightRotate(Node node){
    Node y=node.left;
    Node right=y.right;
    y.right=node;
    node.left=right;
    //维护高度
    node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
    y.height=Math.max(getHeight(y.right),getHeight(y.left))+1;
    return y;
}

2.2.4左旋转

/**
     * 对以x为根的二叉树进行左旋转
     * @param x
     * @return
     */
    private Node leftRotate(Node x){
        Node y=x.right;
        Node left=y.left;
        y.left=x;
        x.right=left;
        //维护高度
        x.height=Math.max(getHeight(x.left), getHeight(x.right))+1;
        y.height=Math.max(getHeight(y.left), getHeight(y.right))+1;
        return y;
    }

2.2.5添加节点

基本思想:

​ 1.向以root为根的平衡二叉树中插入节点。

​ 2.如果当前访问的节点为空,那么将节点插入到该位置。

​ 3.如果待插入节点大于当前正访问的节点,则向右子树中进行添加。

​ 4.如果待插入节点小于当前正访问的节点,则向左子树种进行添加。

​ 5.得到插入完节点之后的树的根节点,同时对根节点进行平衡性的判断和维护。

/**
     * 向avl树中添加一个节点,并返回添加节点后的avl树的根
     * 在添加节点的同时需要维护每个节点的高度
     * 并且需要判断每个节点的平衡因子是否满足要求
     * @param root
     * @param key
     * @param value
     * @return
     */
    private Node add(Node root, K key, V value) {
        if(root==null){
            size++;
            return new Node(key,value);
        }
        if(key.compareTo(root.key)<0){
            root.left=add(root.left,key,value);
        }else if(key.compareTo(root.key)>0){
            root.right=add(root.right,key,value);
        }else{//key==root.key
            root.value=value;
        }
        root.height=Math.max(getHeight(root.left), getHeight(root.right))+1;
        int balanceFactor=getBalanceFactor(root);
        //平衡处理
        //balanceFactor>1 为了判断是否需要进行平衡处理
        //getBalanceFactor(root.left)判断当前是否是LL型,如果是需要右旋转
        if(balanceFactor>1&&getBalanceFactor(root.left)>=0){
            return rightRotate(root);
        }
        if(balanceFactor<-1&&getBalanceFactor(root.right)<=0){
            return leftRotate(root);
        }
        //LR型
        if(balanceFactor>1&&getBalanceFactor(root.left)<0){
            //将LR型转化为LL型
            root.left = leftRotate(root.left);
            return rightRotate(root);
            
        }
        //RL型
        if(balanceFactor<-1&&getBalanceFactor(root.right)>0){
            //将RL转化为RR
             root.right = rightRotate(root.right);
             return leftRotate(root);
        }
        return root;
    }
    

2.2.6删除指定节点

基本思想:

​ 1.删除以root为根的平衡二叉树中的指定节点。

​ 2.如果指定的节点是当前正在访问的节点,那么进行删除操做。

​ 删除操作:

​ 2.1如果该节点没有左子树也没有右子树

​ 2.2如果该节点既有左子树也有右子树

​ 2.3如果该节点只有左子树

​ 2.4如果该节点只有右子树

​ 3.如果指定节点小于当前访问节点,那么去左子树中寻找。

​ 4.如果指定节点大于当前访问节点,那么去右子树中寻找。

​ 5.对于删除节点之后,返回的根节点进行平衡性的判断,如果不平衡进行平衡操作

/**
     * 从AVL树中移除指定节点,并返回移除该节点之后的AVL树的根
     * @param root
     * @param key
     * @return
     */
    private Node remove(Node root, K key) {
        //树为空找不到key节点直接返回空
        if(root==null){
            return null;
        }
        Node retNode;
        if(key.equals(root.key)){
            if(root.left==null&&root.right==null){
                size--;
                retNode= null;
            }
            else if(root.left!=null&&root.right!=null){
                Node min=getMin(root.right);
                Node right=remove(root.right,min.key);
                root.key=min.key;
                root.value=min.value;
                root.right=right;
                retNode =root;
            }else if(root.right!=null){
                Node right=root.right;
                root.right=null;
                size--;
                retNode =right;
            }else{//root.left!=null
                Node left=root.left;
                root.left=null;
                size--;
                retNode =left;
            }
        }else if(key.compareTo(root.key)<0){
            root.left=remove(root.left,key);
            retNode=root;
        }else{//key>root.key
            root.right=remove(root.right,key);
            retNode =root;
        }
        //当删除的节点为叶子节点,那么不需要进行平衡了因为叶子节点没有左右子树
        if(retNode ==null){
            return retNode;
        }
        
        root.height=Math.max(getHeight(retNode.left), getHeight(retNode.right))+1;
        int balanceFactor=getBalanceFactor(retNode);
        //平衡处理
        //balanceFactor>1 为了判断是否需要进行平衡处理
        //getBalanceFactor(root.left)判断当前是否是LL型,如果是需要右旋转
        if(balanceFactor>1&&getBalanceFactor(retNode.left)>=0){
            return rightRotate(retNode);
        }
        if(balanceFactor<-1&&getBalanceFactor(retNode.right)<=0){
            return leftRotate(retNode);
        }
        //LR型
        if(balanceFactor>1&&getBalanceFactor(retNode.left)<0){
            //将LR型转化为LL型
            retNode.left = leftRotate(retNode.left);
            return rightRotate(retNode);
            
        }
        //RL型
        if(balanceFactor<-1&&getBalanceFactor(retNode.right)>0){
            //将RL转化为RR
            retNode.right = rightRotate(retNode.right);
             return leftRotate(retNode);
        }
        return retNode;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值