平衡二叉树(AVL)树详解

1.二叉排序树可能存在的问题

给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST,如下图
在这里插入图片描述
可以看到这是二叉排序树存在许多的问题,那怎么解决呢?用平衡二叉树(AVL)来解决.

2.平衡二叉树基本介绍

(1)平衡二叉树也叫做平衡二叉搜索树(Self-balancing binary search tree)又称为AVL树,可以保证查询效率较高。
(2)具有以下特点:它是一颗空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。
(3)举例说明,看看下面哪些是AVL树。
在这里插入图片描述

3.平衡二叉树的左旋转

(1)要求:给您一个数列,创建处对应的平衡二叉树,数列{4,3,6,5,7,8}
(2)示意图:
在这里插入图片描述
(3)代码实现:

//左旋转
public void leftRotate() {
    //1.创建新的节点,以当前根节点的值
    Node newNode = new Node(this.value);
    //2.把新节点的左子树设置为当前节点的左子树
    newNode.left = this.left;
    //3.把新节点的右子树设置成为当前节点的右子树的左子树
    newNode.right = this.left.right;
    //4.把当前节点的值换为右子节点的值
    this.value = this.right.value;
    //5.把当前节点的右子树设置成当前节点的右子树的右子树
    this.right = this.right.right;
    //6.把当前节点的左子树设置为新节点
    this.left = newNode;
}

4.平衡二叉树的右旋转

(1)要求;给你一个数列,创建对应的平衡二叉树,数列{10,12,8,9,7,6}
(2)示意图:
在这里插入图片描述
(3)代码实现

//右旋转
 public void rightRotate() {
     //1.创建一个新的节点newNode(以根节点的值10创建),创建一个新的节点,值等于当前根节点的值,new  Node(this.value)
     Node newNode = new Node(this.value);
     //2.把新节点的右子树设置了当前节点的右子树,newNode.right=this.right
     newNode.right = this.right;
     //3.把新节点的左子树设置成当前节点的左子树的右子树,newNode.left = this.left.right
     newNode.left = this.left.right;
     //4.把当前节点的值换成当前节点左子节点的值,this.value = this.left.value
     this.value = this.left.value;
     //5.把当前节点的左子树设置成当前节点的左子树的左子树,this.left = this.left.left
     this.left = this.left.left;
     //6.把当前节点的右子树设置成新节点,this.right=newNode
     this.right = newNode;
 }

5.平衡二叉树完整代码

class AvlTree {
    private Node root;

    public Node getRoot() {
        return root;
    }

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

    //创建二叉排序树
    public void add(Node node) {
        if (root == null) {
            root = node;
            return;
        }
        root.addNode(node);
        //当添加完一个节点后,如果右子树的高度-左子树的高度 > 1 且当前节点的右子节点的右子树高度大于右子节点的左子树高度,则左旋转
        if (this.root.getRightHigh() - this.root.getLeftHigh() > 1 && this.root.getRight().getRightHigh() > this.root.getRight().getLeftHigh()) {
            this.root.leftRotate();
        }
        //当添加完一个节点后,如果右子树的高度-左子树的高度 > 1 且当前节点的右子节点的右子树高度大于右子节点的左子树高度,则左旋转
        if (this.root.getLeftHigh() - this.root.getRightHigh() > 1 && this.root.getLeft().getLeftHigh()> this.root.getRight().getRightHigh()) {
            this.root.rightRotate();
        }
    }

    //中序遍历
    public void infixOrder() {
        if (root != null) {
            this.root.infixOrder();
            return;
        }
        throw new NullPointerException("根节点为空");
    }
}

//二叉排序树的节点
class Node {
    private int value;
    private Node left;
    private Node right;

    public Node(int value) {
        this.value = value;
    }

    public Node() {
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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

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

    /**
     * 向二叉排序树中添加节点
     *
     * @param node 要添加的节点
     */
    public void addNode(Node node) {
        if (node == null) {
            throw new NullPointerException("节点为空");
        }
        //判断当前节点的值是否大于添加节点
        if (this.value > node.value) {
            //判断当前节点的左子节点是否为空
            if (this.left == null) {
                this.left = node;
                return;
            }
            //递归
            this.left.addNode(node);
            return;
        }
        //判断当前节点的右子节点是否为空
        if (this.right == null) {
            this.right = node;
            return;
        }
        //递归
        this.right.addNode(node);
    }

    //返回当前节点的高度
    public int getTreeHigh() {
        return Math.max(this.left == null ? 0 : this.left.getTreeHigh(), this.right == null ? 0 : this.right.getTreeHigh()) + 1;
    }

    //返回左子树的高度
    public int getLeftHigh() {
        if (this.left == null) {
            return 0;
        }
        return this.left.getTreeHigh();
    }

    //返回右子树的高度
    public int getRightHigh() {
        if (this.right == null) {
            return 0;
        }
        return this.right.getTreeHigh();
    }

    //左旋转
    public void leftRotate() {
        //1.创建新的节点,以当前根节点的值
        Node newNode = new Node(this.value);
        //2.把新节点的左子树设置为当前节点的左子树
        newNode.left = this.left;
        //3.把新节点的右子树设置成为当前节点的右子树的左子树
        newNode.right = this.left.right;
        //4.把当前节点的值换为右子节点的值
        this.value = this.right.value;
        //5.把当前节点的右子树设置成当前节点的右子树的右子树
        this.right = this.right.right;
        //6.把当前节点的左子树设置为新节点
        this.left = newNode;
    }
   //右旋转
    public void rightRotate() {
        //1.创建一个新的节点newNode(以根节点的值10创建),创建一个新的节点,值等于当前根节点的值,new  Node(this.value)
        Node newNode = new Node(this.value);
        //2.把新节点的右子树设置了当前节点的右子树,newNode.right=this.right
        newNode.right = this.right;
        //3.把新节点的左子树设置成当前节点的左子树的右子树,newNode.left = this.left.right
        newNode.left = this.left.right;
        //4.把当前节点的值换成当前节点左子节点的值,this.value = this.left.value
        this.value = this.left.value;
        //5.把当前节点的左子树设置成当前节点的左子树的左子树,this.left = this.left.left
        this.left = this.left.left;
        //6.把当前节点的右子树设置成新节点,this.right=newNode
        this.right = newNode;
    }
    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

}

6.平衡二叉树的双旋转

前面两个数列,进行单旋转(记一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是再某些情况下,单旋转不能完成平衡二叉树的转换。比如
int[] array={10,11,7,6,8,9}或者int[] array={2,1,6,5,7,3},进过一次旋转,非平衡二叉树并没有转为平衡二叉树。
问题分析:在这里插入图片描述
改进之后的代码:

public void add(Node node) {
    if (root == null) {
        root = node;
        return;
    }
    root.addNode(node);
    //当添加完一个节点后,如果右子树的高度-左子树的高度 > 1 ,则进行左旋转
    if (this.root.getRightHigh() - this.root.getLeftHigh() > 1 ) {
        //当前节点的右子节点的左子树高度大于右子节点的右子树高度,则先对当前节点的右子节点进行右旋转
        if (this.root.getRight().getRightHigh() < this.root.getRight().getLeftHigh()) {
            this.root.getRight().rightRotate();
            //然后再对当前节点进行左旋转
            this.root.leftRotate();
            return;
        }
        this.root.leftRotate();
    }
    //当添加完一个节点后,如果左子树的高度-右子树的高度 > 1 ,则进行右旋转
    if (this.root.getLeftHigh() - this.root.getRightHigh() > 1 ) {
        //当前节点的左子节点的右子树高度大于左子节点的左子树高度,则先对当前节点的左子节点进行左旋转
        if (this.root.getLeft().getLeftHigh() < this.root.getLeft().getRightHigh()) {
            this.root.getLeft().leftRotate();
            //然后再对当前节点进行右旋转
            this.root.rightRotate();
            return;
        }
        this.root.rightRotate();
    }
}
展开阅读全文
©️2019 CSDN 皮肤主题: 猿与汪的秘密 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读