AVL树(带有平衡条件的二叉查找树)

一、给你一个数列{1,2,3,4,5,6},要求创建一棵二叉排序树$BST$树,如下图所示。

 

 

 

 

 

 

 

 

该$BST$存在一些问题,如下:

  • 左子树全部为空,从形式上看更像一个单链表;
  • 查询比单链表慢,因为查询时还要比较左子树;
  • 这个BST树是不平衡的,解决方案为改用平衡条件的二叉查找树(AVL);

 

二、平衡二叉树

平衡二叉树也叫平衡二叉搜索树,也被称为AVL树。它可以保证较高的查询效率。AVL树具有以下特点:它是一棵空树或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL算法(不同于AVL树)、伸展树等。

下面是单旋转的情况,以左旋转为例,图示如下:

 

这是一个右子树的高度比左子树的高度高很多

左旋转

  • 创建一个新的节点newNode,值等于当前根节点的值(以4这个值创建)
  • 把新节点的左子树设置为当前节点的左子树
  • 把新节点的右子树设置为当前节点的右子树的左子树
  • 把当前节点的值替换为其右子节点的值
  • 把当前节点的左子树设置为新节点
  • 把当前节点的右子树设置为其右子树的右子树

代码如下:

    //单旋转(以左旋转为例)
    public void leftRotate() {
        //创建新节点,值为当前根节点的值
        node newNode = new node(this.number);
        //把新的节点的左子节点设置成当前节点的左子节点
        newNode.left = this.left;
        //把新节点的右子节点设置为当前节点的右子节点的左子节点
        newNode.right = this.right.left;
        //把当前节点的值替换为其右子节点的值
        this.number = this.right.number;
        //把当前节点的右子节点改为右子节点的右子节点
        this.right = this.right.right;
        //把当前节点的左子节点改为新的节点
        this.left = newNode;
    }

 

如果是根的左子树比右子树高呢? arr = {10,12,8,9,7,6};

二叉树的左子树的高度比右子树的高度高很多,需要进行右旋转。

  • 创建一个新节点,值等于当前根节点的值
  • 新节点的右子树为当前节点的右子树
  • 新节点的左子树为当前节点的左子树的右子树
  • 当前节点的值替换为其左子节点的值
  • 当前节点的左子树为其左子树的左子树
  • 当前节点的右子树为新的节点

代码:

    //单旋转(以右旋转为例)
    public void rightRotate() {
        node newNode = new node(this.number);
        newNode.right = this.right;
        newNode.left = this.left.right;

        this.number = this.left.number;
        this.left = this.left.left;
        this.right = newNode;
    }

 

三、双旋转

在某些情况下,单旋转不能完成平衡二叉树的转换。如下图:

这种情况下,直接进行右旋转,并不能达到平衡的效果。

分析一下:

  • 如果根的左子树的右子树大于根的左子树的左子树
  • 应该先对当前节点的左子节点进行左旋转
  • 然后对当前节点进行右旋转

代码:

        if (getRoot().leftHeight() - getRoot().rightHeight() > 1) { //根节点的左子树的高度与右子树的高度差大于1
            if (getRoot().left.rightHeight() > getRoot().left.leftHeight()) {
                //对当前节点的左子节点进行左旋转
                getRoot().left.leftRotate();
                getRoot().rightRotate();
            } else {
                getRoot().rightRotate();    //右旋转
            }
            return ;    //这个加上,避免退出后再进入下一个左旋转
        }

        //左旋转
        if (getRoot().rightHeight() - getRoot().leftHeight() > 1) { //根节点的右子树的高度与左子树的高度差大于1
            if (getRoot().right.leftHeight() > getRoot().right.rightHeight()) {
                getRoot().right.rightRotate();  //右子节点进行右旋转
                getRoot().leftRotate();
            } else {
                getRoot().leftRotate(); //左旋转
            }
        }

完整代码:

package AVL;
import java.util.EmptyStackException;

public class AVLTreeDemo {
    public static void main(String[] args) {
        int[] arr = {10,11,7,6,8,9};
        //创建一个AVLTree对象
        AVLTree avltree = new AVLTree();
        //添加节点
        for (int i = 0; i < arr.length; i++) {
            avltree.insert(arr[i]);
        }
        //遍历
        System.out.println("中序遍历:");
        avltree.printTree();

        //平衡处理后
        System.out.println("平衡处理后:");
        System.out.println("树的高度:" + avltree.getRoot().height());
        System.out.println("左子树的高度:" + avltree.getRoot().leftHeight());
        System.out.println("右子树的高度:" + avltree.getRoot().rightHeight());
        System.out.println("根节点的值为:" + avltree.getRoot());

        System.out.println("根节点的左子节点为:" + avltree.getRoot().left);
        System.out.println("根节点的右子节点为:" + avltree.getRoot().right);
    }
}
class AVLTree {
    private node root; //根节点就能确定一棵二叉树
    public void setRoot(node root) {
        this.root = root;
    }

    public node getRoot() {
        return root;
    }

    //BST的构造函数
    public AVLTree() { this.root = null; }

    //建立空树
    public void makeEmpty() { this.root = null; }

    //判断是否为空
    public boolean isEmpty() { return this.root == null; }

    //判断是否包含某一节点
    public boolean contains(int x) {
        return contains(x, this.root);
    }

    //寻找二查搜索树上的最小值
    public int findMin() {
        if (isEmpty()) { throw new EmptyStackException(); }
        return findMin(root).number;
    }

    //寻找二查搜索树上的最大值
    public int findMax() {
        if (isEmpty()) { throw new EmptyStackException(); }
        return findMax(root).number;
    }

    //插入节点
    public void insert(int x) {
        root = insert(x, root);

        //右旋转
        if (getRoot().leftHeight() - getRoot().rightHeight() > 1) { //根节点的左子树的高度与右子树的高度差大于1
            if (getRoot().left.rightHeight() > getRoot().left.leftHeight()) {
                //对当前节点的左子节点进行左旋转
                getRoot().left.leftRotate();
                getRoot().rightRotate();
            } else {
                getRoot().rightRotate();    //右旋转
            }
            return ;    //这个加上,避免退出后再进入下一个左旋转
        }

        //左旋转
        if (getRoot().rightHeight() - getRoot().leftHeight() > 1) { //根节点的右子树的高度与左子树的高度差大于1
            if (getRoot().right.leftHeight() > getRoot().right.rightHeight()) {
                getRoot().right.rightRotate();  //右子节点进行右旋转
                getRoot().leftRotate();
            } else {
                getRoot().leftRotate(); //左旋转
            }
        }
    }

    //删除节点
    public void remove(int x) {
        root = remove(x, root);
    }

    //打印二查搜索树(前序遍历)
    public void printTree() {
        if (isEmpty())
            System.out.println("Empty tree");
        else
            printTree(this.root);
    }

    private boolean contains(int x, node t) {
        //如果t是空集,就返回false
        if(t == null) {
            return false;
        }
        //注意:compareTo的用法,可以判断两个元素间的三种情况
        int compareResult = x - t.number;

        if (compareResult < 0) {    //注意:这里可以用尾递归优化
            return contains(x, t.left);
        } else if (compareResult > 0) {
            return contains(x, t.right);
        } else
            return true;
    }

    private node findMin(node t) {
        //findMin方法的递归实现
        if (t == null)
            return null;
        else if (t.left == null)
            return t;
        return findMin(t.left);
    }

    private node findMax(node t) {
        //findMax方法的非递归实现(while循环,一直寻找最右端的节点)
        if (t != null) {
            while (t.right != null) {
                t = t.right;
            }
        }
        return t;
    }

    private node insert(int x, node t) {
        if (t == null) {
            return new node(x, null, null);
        }

        int compareResult = x - t.number;

        if (compareResult < 0) {
            t.left = insert(x, t.left);
        } else if (compareResult > 0){
            t.right = insert(x, t.right);
        } else
            System.out.println("The insert Node has already exist, so we will do noting.");

        return t;
    }

    private node remove(int x, node t) {
        if (t == null) { return t; }

        int compareResult = x- t.number;

        if (compareResult < 0) {
            t.left = remove(x, t.left);
        } else if(compareResult > 0) {
            t.right = remove(x, t.right);
        }else if (t.left != null && t.right != null) {  // 有两个子节点
            t.number = findMin(t.right).number;   //找到右子树上最小的一个节点
            t.right = remove(t.number, t.right);
        }else   //至多有一个子节点(如果没有子节点,直接用null删除即可;反之,用子节点替代)
            t = (t.left != null) ? t.left: t.right;

        return t;
    }

    private void printTree(node t) { //中序遍历
        if (t != null) {
            printTree(t.left);
            System.out.println(t.number);
            printTree(t.right);
        }
    }

    //前序遍历
    public void preOrder() {
        if(this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("当前二叉树为空,无法进行前序遍历");
        }
    }

    //中序遍历
    public void infixOrder() {
        if(this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("当前二叉树为空,无法进行中序遍历");
        }
    }

    //后序遍历
    public void postOrder() {
        if(this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("当前二叉树为空,无法进行后序遍历");
        }
    }
}

//节点
class node {
    public int number;
    public node left;
    public node right;

    public node(int number, node left, node right) {
        this.number = number;
        this.left = left;
        this.right = right;
    }

    public int leftHeight() {
        if (left == null) { return 0;}
        return left.height();
    }

    public int rightHeight() {
        if (right == null) { return 0;}
        return right.height();
    }

    //以当前节点为根节点的树的高度
    public int height() {
        return Math.max(left == null? 0:left.height(), right == null? 0: right.height()) + 1;   //加1表示加上当前节点的高度
    }

    //leftType = 0 说明连接的是左子树;leftType = 1 说明连接的是前驱节点
    private int leftType;

    //rightType = 0 说明连接的是右子树;rightType = 1 说明连接的是后继节点
    private int rightType;

    public node(int number) {
        this.number = number;
    }

    public node getLeft() {
        return left;
    }

    public node getRight() {
        return right;
    }

    public int getLeftType() {
        return leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public void setNumber(int number) {
        this.number = number;
    }


    public void setLeft(node left) {
        this.left = left;
    }

    public void setRight(node right) {
        this.right = right;
    }

    //输出当前节点的信息
    @Override
    public String toString() {
        return "Node{" +
                "number=" + number  +
                '}';
    }
    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) { this.left.preOrder(); }
        if (this.right != null) { this.right.preOrder(); }
    }

    //中序遍历
    public void infixOrder() {
        if (this.left != null) { this.left.infixOrder(); }
        System.out.println(this);
        if (this.right != null) { this.right.infixOrder(); }
    }

    //后序遍历
    public void postOrder() {
        if (this.left != null) { this.left.postOrder(); }
        if (this.right != null) { this.right.postOrder(); }
        System.out.println(this);
    }

    //单旋转(以左旋转为例)
    public void leftRotate() {
        //创建新节点,值为当前根节点的值
        node newNode = new node(this.number);
        //把新的节点的左子节点设置成当前节点的左子节点
        newNode.left = this.left;
        //把新节点的右子节点设置为当前节点的右子节点的左子节点
        newNode.right = this.right.left;
        //把当前节点的值替换为其右子节点的值
        this.number = this.right.number;
        //把当前节点的右子节点改为右子节点的右子节点
        this.right = this.right.right;
        //把当前节点的左子节点改为新的节点
        this.left = newNode;
    }

    //单旋转(以右旋转为例)
    public void rightRotate() {
        node newNode = new node(this.number);
        newNode.right = this.right;
        newNode.left = this.left.right;

        this.number = this.left.number;
        this.left = this.left.left;
        this.right = newNode;
    }
}

 

posted @ 2021-01-28 11:17  Peterxiazhen  阅读(136)  评论(0编辑  收藏  举报