用二分搜索树(BST)实现平衡二叉树(AVL)

Posted 码道小鑫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用二分搜索树(BST)实现平衡二叉树(AVL)相关的知识,希望对你有一定的参考价值。

当前浏览器不支持播放音乐或语音,请在微信或其他浏览器中播放

平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。平衡二叉树是为了解决二分搜索树中节点都偏向一侧导致退化成链表的问题提出的数据结构。

具体实现如下:

  1. 二分搜索树的实现:

    package cn.dataStructures.BinarySerachTree;

    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Stack;

    public class BST1 <E extends Comparable<E>>{
        private class Node{
            public Node left,right;
            public E e;
            public Node(E e){
                this.e=e;
                left=null;
                right=null;
            }
        }
        public int size;
        public Node root;
        public BST1(){
            root=null;
            size=0;
        }
        public int size(){
            return size;
        }
        public boolean isEmpty(){
            return size==0;
        }
        //向树中添加元素
        public void add(E e){
    //        if(root==null){
    //            root=new Node(e);
    //            size++;
    //            return;
    //        }
            root=add(root,e);
            
        }
        private Node add(Node node,E e){
            if(node==null){
                size++;
                return new Node(e);
            }
            else if(e.compareTo(node.e)<0){
                Node left=add(node.left,e);//如果小于树中的元素,则向该节点的左子树插入元素,此节点为根节点,
                //再继续判断是否为空,如果是直接创建新节点并将值插入
                size++;
            }
            else if(e.compareTo(node.e)>0){
                Node right=add(node.right,e);
                size++;
            }
            return node;        
        }
        //查询元素
        public boolean contains(E e){
            return contains(root,e);
        }
        private boolean contains(Node node,E e){
            if(node==null){
                return false;
            }
            if(e.compareTo(node.e)==0){
                return true;
            }
            else if(e.compareTo(node.e)>0){
                return contains(node.right,e);
                
            }
            else if(e.compareTo(node.e)<0){
                return contains(node.left,e);
            }
            return false;
        
        }
        //遍历元素--前序遍历---先遍历节点元素--》左子树--》右子树
        public void preOrder(){
            preOrder(root);
        }
        public void preOrder(Node node){
            if(node==null){
                return;
            }
            System.out.println(node.e);
            preOrder(node.left);
            preOrder(node.right);
        }
        //前序遍历非递归----利用栈结构
        public void preOrderNR(){
            Stack<Node> stack=new Stack<>();
            stack.push(root);
            while(!stack.isEmpty()){
                Node cur=stack.pop();
                System.out.println(cur.e);
                //因为栈是后进先出的所以要先压入右子树,在压入左子树,这样再取出元素时就会先取出左孩子节点然后取出右孩子节点
                if(cur.right!=null){
                    stack.push(cur.right);
                }
                if(cur.left!=null){
                    stack.push(cur.left);
                }
            }
            
        }
        //    前序遍历---层序遍历(广度优先遍历)-队列
        public void leveOrder(){
            Queue<Node> q=new LinkedList<>();
            q.add(root);
            while(!q.isEmpty()){
                Node cur=q.remove();
                System.out.println(cur.e);
                if(cur.left!=null){
                    q.add(cur.left);
                    
                }
                if(cur.right!=null){
                    q.add(cur.right);
                }
            }        
        }
        //中序遍历--先遍历左子树--》节点元素--》右子树
        public void inOrder(){
            inOrder(root);
        }
        public void  inOrder(Node node){
            if(node==null){
                return;
            }
            inOrder(node.left);
            System.out.println(node.e);
            inOrder(node.right);
        }
        //后序遍历--先遍历左子树--》右子树--》节点元素
        public void postOrder(){
            postOrder(root);
        }
        public void postOrder(Node node){
            if(node==null){
                return;
            }
            postOrder(node.left);
            postOrder(node.right);
            System.out.println(node.e);
        }
        //寻找最小节点
        public E minimum(){
            if(size==0){
                throw new IllegalArgumentException("树为空!");            
            }
            Node minNode=minimum(root);
            return minNode.e;
        }
        private Node minimum(Node node){
            if(node.left==null){
                return node;
            }
            return minimum(node.left);
        }
        //寻找最大节点
        public E maximum(){
            if(size==0){
                throw new IllegalArgumentException("树为空!");            
            }
            Node maxNode=maximum(root);
            return maxNode.e;
        }
        private Node maximum(Node node){
            if(node.right==null){
                return node;
            }
            return maximum(node.right);
        }
        //删除二叉树中最小值所在的节点,并返回这个值
        public E removeMin(){
            E ret=minimum();//调用寻找最小节点的方法(其中已经包含根结点)
            root=removeMin(root);
            return ret;
        }
        private Node removeMin(Node node){
            //删除以node为跟根的二叉树最小节点
            if(node.left==null){
                Node rightNode=node.right;
                node.right=null;
                size--;
                return rightNode;
            }
            removeMin(node.left);
            return node;//返回删除最小节点后二叉树新的根
        }
        //删除二叉树最大值所在的节点并返回这个值
        public E removeMax(){
            E ret=maximum();
            root=removeMax(root);
            return ret;
        }
        private Node removeMax(Node node){
            if(node.right==null){
                Node leftNode=node.left;
                node.left=null;
                size--;
                return leftNode;
            }
            removeMax(node.right);
            return node;
        }
        //删除树中任意位置的节点
        //思想:先找到这个要删除的节点,通过元素的比较来找该节点,
        //找到该节点后需要对其判断该节点的左子树和右子树是否为空,或者都不为空,这三种情况分别进行操作
        public void remove(E e){
            root=remove(root,e);
        }
        private Node remove(Node node,E e){
            if(node==null){
                return null;
            }
            if(e.compareTo(node.e)<0){
                node.left=remove(node.left,e);    
                return node;//返回新的二叉树的根    
            }
            if(e.compareTo(node.e)>0){
                node.right=remove(node.right,e);
                return node;
            }
            else//e.compareTo(node.e)==0
            {
                if(node.left==null){
                    Node rightNode=node.right;
                    size--;
                    node.right=null;
                    return rightNode;//将rightNode的节点设为最新二叉树的根节点
                }
                if(node.right==null){
                    Node leftNode=node.left;
                    size--;
                    node.left=null;
                    return leftNode;
                }
                else{
                    Node successor=new Node(minimum(node.right).e);//把改节点右子树的最小节点赋值给successor
                    size++;
                    successor.right=removeMin(node.right);//吧succssor的右子树设置为要删除节点的右子树
                    successor.left=node.left;//把要删除的节点node的左子树赋值给successor的左子树
                    node.left=node.right=null;//将要删除的左子树和右子树置空
                    size--;
                    return successor;
                }
            }
        }
    }

    2利用BST实现AVL

    代码如下:

package cn.dataStructures.AVL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**
 * 平衡二叉树:1
 * 1、首先是二分搜索树,即小与节点的元素放在节点的左侧-左孩子,大于该节点的节点放在节点的右侧--右孩子
 * 2.对于任意一个节点,该节点的左子树和右子树的高度差的绝对值不能超过1,节点的左右子树的高度差的绝对值为
 *     平衡因子,对于叶子节点而言,它的平衡因子为0。
 * @author Administrator
 *
 * @param <E>
 */
public class AVLTree<E extends Comparable<E>> {
    private class Node{
        public Node left,right;
        public E e;
        public int hight;
        public Node(E e){
            this.e=e;
            hight=1;//对于节点的高度而言,只有当执行插入操作时hight才会发生改变,而插入时一般插入的是叶子节点,
                    //    而叶子节点的初始高度为1
            left=null;
            right=null;
        }
    }
    public int size;
    public Node root;
    public AVLTree(){
        root=null;
        size=0;
    }
    public int size(){
        return size;
    }
    public boolean isEmpty(){
        return size==0;
    }
    
    //获取任意节点的高度值
    private int getHight(Node node){
        if(node==null)
            return 0;
        return node.hight;
    
    }
    //计算节点node的平衡因子
    private int getBalanceFactor(Node node){
        if(node==null)
            return 0;
        return getHight(node.left)-getHight(node.right);
        
    }
    //判断是不是一个二分搜索树BST--对树进行中序遍历,输出元素为从小到大的排列
    public boolean isBST(){
        ArrayList<E> list=new ArrayList<>();
        inOrder(root,list);
        for(int i=0;i<list.size();i++){
            if(list.get(i-1).compareTo(list.get(i))>0)
                return false;
        }
        return true;
    }
    private void inOrder(Node node,ArrayList<E> list){
        if(node==null)
            return;
        inOrder(node.left,list);
        list.add(node.e);
        inOrder(node.right,list);
        
    }
    //判断是不是一个平衡二叉树--利用递归
    public boolean isBalance(){
        return isBalance(root);
    }
    private boolean isBalance(Node node){
        if(node==null)
            return true;
        int balance=getBalanceFactor(node);
        if(Math.abs(balance)>1)
            return false;
        return isBalance(node.left)&&isBalance(node.right);
        
    }
    //右旋转--此处Node节点为不平衡节点,即BF>1,需要被旋转的节点
    private Node rightRotate(Node y){
        Node x=y.left;
        Node T3=x.right;
        x.right=y;
        y.left=T3;
        //更新x和y的hight值,因为只对x和y节点进行了旋转,需要先更新y的值再更新x的值--因为此时y是x的右孩子
        //x的高度与y的高度相关的
        y.hight=1+Math.max(getHight(y.left), getHight(y.right));
        x.hight=1+Math.max(getHight(x.left), getHight(x.right));
        
        return x;//返回根节点x--此时x为根节点
    }
    //左旋转
    private Node leftRotate(Node y){
        Node x=y.right;
        Node T2=x.left;
        x.left=y;
        y.right=T2;
        //更新y和x的高度
        y.hight=1+Math.max(getHight(y.left), getHight(y.right));
        x.hight=1+Math.max(getHight(x.left), getHight(x.right));
        return x;
        
    }
    //向树中添加元素
    public void add(E e){
//        if(root==null){
//            root=new Node(e);
//            size++;
//            return;
//        }
        root=add(root,e);
        
    }
    private Node add(Node node,E e){
        if(node==null){
            size++;
            return new Node(e);
        }
        else if(e.compareTo(node.e)<0){
            Node left=add(node.left,e);//如果小于树中的元素,则向该节点的左子树插入元素,此节点为根节点,
            //再继续判断是否为空,如果是直接创建新节点并将值插入
            size++;
        }
        else if(e.compareTo(node.e)>0){
            Node right=add(node.right,e);
            size++;
        }
        //更新hight==该节点的左右子树的最大高度值加1
        node.hight=1+Math.max(getHight(node.left), getHight(node.right));
        //计算平衡因子
        int balanceFactor=getBalanceFactor(node);
        if(Math.abs(balanceFactor)>1)
            System.out.println("该节点是不平衡的"+balanceFactor);
        //平衡维护--LL
        if(balanceFactor>1&&getBalanceFactor(node.left)>=0)//首先判断平衡因子是不是大于1,
            //大于一说明树已经不平衡需要维护,然后判断不平衡节点的左子树的平衡因子是否为正数,即符号是否一致,如果为正则右旋转
            return rightRotate(node);
        //平衡维护--RR
        if(balanceFactor<-1&&getBalanceFactor(node.right)<=0)
            return leftRotate(node);
        //平衡维护--LR
        if(balanceFactor>1&&getBalanceFactor(node.left)<0){
            node.left=leftRotate(node.left);//先对节点的左孩子进行左旋转
            return rightRotate(node);//在对节点进行右旋转
        }
        //平衡维护--RL
        if(balanceFactor<-1&&getBalanceFactor(node.right)>0){
            node.right=rightRotate(node.right);//先对node的右孩子进行右旋转
            return leftRotate(node);    //再对该节点进行左旋转
            
        }
        return node;        
    }
    //查询元素
    public boolean contains(E e){
        return contains(root,e);
    }
    private boolean contains(Node node,E e){
        if(node==null){
            return false;
        }
        if(e.compareTo(node.e)==0){
            return true;
        }
        else if(e.compareTo(node.e)>0){
            return contains(node.right,e);
            
        }
        else if(e.compareTo(node.e)<0){
            return contains(node.left,e);
        }
        return false;
    
    }
    //遍历元素--前序遍历---先遍历节点元素--》左子树--》右子树
    public void preOrder(){
        preOrder(root);
    }
    public void preOrder(Node node){
        if(node==null){
            return;
        }
        System.out.println(node.e);
        preOrder(node.left);
        preOrder(node.right);
    }
    //前序遍历非递归----利用栈结构
    public void preOrderNR(){
        Stack<Node> stack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            Node cur=stack.pop();
            System.out.println(cur.e);
            //因为栈是后进先出的所以要先压入右子树,在压入左子树,这样再取出元素时就会先取出左孩子节点然后取出右孩子节点
            if(cur.right!=null){
                stack.push(cur.right);
            }
            if(cur.left!=null){
                stack.push(cur.left);
            }
        }
        
    }
    //    前序遍历---层序遍历(广度优先遍历)-队列
    public void leveOrder(){
        Queue<Node> q=new LinkedList<>();
        q.add(root);
        while(!q.isEmpty()){
            Node cur=q.remove();
            System.out.println(cur.e);
            if(cur.left!=null){
                q.add(cur.left);
                
            }
            if(cur.right!=null){
                q.add(cur.right);
            }
        }        
    }
    //中序遍历--先遍历左子树--》节点元素--》右子树
    public void inOrder(){
        inOrder(root);
    }
    public void  inOrder(Node node){
        if(node==null){
            return;
        }
        inOrder(node.left);
        System.out.println(node.e);
        inOrder(node.right);
    }
    //后序遍历--先遍历左子树--》右子树--》节点元素
    public void postOrder(){
        postOrder(root);
    }
    public void postOrder(Node node){
        if(node==null){
            return;
        }
        postOrder(node.left);
        postOrder(node.right);
        System.out.println(node.e);
    }
    //寻找最小节点
    public E minimum(){
        if(size==0){
            throw new IllegalArgumentException("树为空!");            
        }
        Node minNode=minimum(root);
        return minNode.e;
    }
    private Node minimum(Node node){
        if(node.left==null){
            return node;
        }
        return minimum(node.left);
    }
    //寻找最大节点
    public E maximum(){
        if(size==0){
            throw new IllegalArgumentException("树为空!");            
        }
        Node maxNode=maximum(root);
        return maxNode.e;
    }
    private Node maximum(Node node){
        if(node.right==null){
            return node;
        }
        return maximum(node.right);
    }
    //删除二叉树中最小值所在的节点,并返回这个值
    public E removeMin(){
        E ret=minimum();//调用寻找最小节点的方法(其中已经包含根结点)
        root=removeMin(root);
        return ret;
    }
    private Node removeMin(Node node){
        //删除以node为跟根的二叉树最小节点
        if(node.left==null){
            Node rightNode=node.right;
            node.right=null;
            size--;
            return rightNode;
        }
        removeMin(node.left);
        return node;//返回删除最小节点后二叉树新的根
    }
    //删除二叉树最大值所在的节点并返回这个值
    public E removeMax(){
        E ret=maximum();
        root=removeMax(root);
        return ret;
    }
    private Node removeMax(Node node){
        if(node.right==null){
            Node leftNode=node.left;
            node.left=null;
            size--;
            return leftNode;
        }
        removeMax(node.right);
        return node;
    }
    //删除树中任意位置的节点
    //思想:先找到这个要删除的节点,通过元素的比较来找该节点,
    //找到该节点后需要对其判断该节点的左子树和右子树是否为空,或者都不为空,这三种情况分别进行操作
    public void remove(E e){
        root=remove(root,e);
    }
    private Node remove(Node node,E e){
        if(node==null){
            return null;
        }
        if(e.compareTo(node.e)<0){
            node.left=remove(node.left,e);    
            return node;//返回新的二叉树的根    
        }
        if(e.compareTo(node.e)>0){
            node.right=remove(node.right,e);
            return node;
        }
        else//e.compareTo(node.e)==0
        {
            if(node.left==null){
                Node rightNode=node.right;
                size--;
                node.right=null;
                return rightNode;//将rightNode的节点设为最新二叉树的根节点
            }
            if(node.right==null){
                Node leftNode=node.left;
                size--;
                node.left=null;
                return leftNode;
            }
            else{
                Node successor=new Node(minimum(node.right).e);//把改节点右子树的最小节点赋值给successor
                size++;
                successor.right=removeMin(node.right);//吧succssor的右子树设置为要删除节点的右子树
                successor.left=node.left;//把要删除的节点node的左子树赋值给successor的左子树
                node.left=node.right=null;//将要删除的左子树和右子树置空
                size--;
                return successor;
            }
        }
    }
}


以上是关于用二分搜索树(BST)实现平衡二叉树(AVL)的主要内容,如果未能解决你的问题,请参考以下文章

解密树的平衡:二分搜索树 → AVL自平衡树 → 红黑树

十三彻底搞懂平衡二叉树AVL

[数据结构]二叉搜索树(BST) VS 平衡二叉排序树(AVL) VS B树(平衡多路搜索树) VS B+树 VS 红黑树(平衡二叉B树)

平衡树——AVL算法

数据结构中常见的树(BST二叉搜索树AVL平衡二叉树RBT红黑树B-树B+树B*树)

数据结构中常见的树(BST二叉搜索树AVL平衡二叉树RBT红黑树B-树B+树B*树)