一、看一个案例(说明二叉排序树可能的问题)

给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在
左边 BST 存在的问题分析:

  1. 左子树全部为空,从形式上看,更像一个单链表.
  2. 插入速度没有影响
  3. 查询速度明显降低(因为需要依次比较), 不能发挥 BST 的优势,因为每次还需要比较左子树,其查询速度比 单链表还慢

解决方案:平衡二叉树(AVL)

二、基本介绍

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

三、应用案例-单旋转(左旋转)

要求: 给你一个数列,创建出对应的平衡二叉树.数列 {4,3,6,5,7,8}
思路分析:
树结构的实际应用-二叉平衡树 - 图2
代码实现:(整合的代码放在最后面)

  1. // Node 节点类中定义一个左旋转方法
  2. class Node {
  3. private int value;
  4. private Node left;
  5. private Node right;
  6. public Node(int value) {
  7. this.value = value;
  8. }
  9. /**
  10. * 左旋转
  11. */
  12. public void leftRotate() {
  13. // 创建一个新节点值 = 当前节点
  14. Node newNode = new Node(this.value);
  15. // 新节点的左节点 = 当前节点的左节点
  16. newNode.setLeft(this.left);
  17. // 新节点的右节点 = 当前节点的右节点的左节点
  18. newNode.setRight(this.right.getLeft());
  19. // 当前节点的左节点 = 新节点
  20. this.left = newNode;
  21. // 当前节点的值 = 右节点的值
  22. this.value = this.right.getValue();
  23. // 当前节点的右节点 = 当前节点的右节点的右节点
  24. this.right = this.right.getRight();
  25. }
  26. public int getValue() {
  27. return value;
  28. }
  29. public void setValue(int value) {
  30. this.value = value;
  31. }
  32. public Node getLeft() {
  33. return left;
  34. }
  35. public void setLeft(Node left) {
  36. this.left = left;
  37. }
  38. public Node getRight() {
  39. return right;
  40. }
  41. public void setRight(Node right) {
  42. this.right = right;
  43. }
  44. @Override
  45. public String toString() {
  46. return "Node{" +
  47. "value=" + value +
  48. '}';
  49. }
  50. }

四、应用案例-单旋转(右旋转)

要求: 给你一个数列,创建出对应的平衡二叉树.数列 {10,12, 8, 9, 7, 6}
思路分析:
树结构的实际应用-二叉平衡树 - 图3
代码实现:

// Node 节点类中定义一个右旋转方法
class Node {

    private int value;
    private Node left;
    private Node right;

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

    /**
      * 右旋转
      */
    public void rightRotate() {
        // 创建一个 newNode 值为当前节点
        Node newNode = new Node(this.value);
        // newNode 的右节点 = 当前节点的右节点
        newNode.setRight(this.right);
        // newNode 的左节点 = 当前节点的左节点的右节点
        newNode.setLeft(this.left.getRight());
        // 当前节点的右节点 = newNode
        this.right = newNode;
        // 当前节点的值 = 当前节点的左节点的值
        this.value = this.left.getValue();
        // 当前节点的左节点 = 当前节点的左节点的左节点
        this.left = this.left.getLeft();
    }

    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;
    }

    @Override
    public String toString() {
        return "Node{" +
            "value=" + value +
            '}';
    }
}

五、应用案例-双旋转

前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转 不能完成平衡二叉树的转换。
比如数列:
int[] arr = { 10, 11, 7, 6, 8, 9 }; 运行原来的代码可以看到,并没有转成 AVL 树
int[] arr = {2,1,6,5,7,3}; // 运行原来的代码可以看到,并没有转成 AVL 树
问题分析:
树结构的实际应用-二叉平衡树 - 图4
int[] arr = { 10, 11, 7, 6, 8, 9 } 在进行右旋转后还是一个非平衡二叉树
解决思路分析:
在满足右旋转条件时,要判断

  1. 如果是左子树的 右子树高度大于左子树的左子树时:
  2. 就是对当前根节点的左子树,先进行 左旋转,
  3. 然后在对当前根节点进行右旋转即可

否则,直接对当前节点(根节点)进行右旋转.即可树结构的实际应用-二叉平衡树 - 图5
代码实现:( AVL 树的完整代码)

public class AVLTreeDemo {

    public static void main(String[] args) {
        //        int[] arr = {4, 3, 6, 5, 7, 8};
        //        int[] arr = {10, 12, 8, 9, 7, 6};
        int[] arr = {10, 11, 7, 6, 8, 9};
        //        int[] arr = {2, 1, 6, 5, 7, 3};
        AVLTree avlTree = new AVLTree();
        for (int i : arr) {
            avlTree.add(new Node(i));
        }
        System.out.println("平衡处理之后的 二叉树:");
        avlTree.infixOrder();
        Node root = avlTree.getRoot();
        System.out.println("root height:" + root.getHeight());
        System.out.println("root left height:" + root.getLeft().getHeight());
        System.out.println("root right height:" + root.getRight().getHeight());
    }
}

class AVLTree {

    private Node root;

    public void add(Node addNode) {
        if (root == null) {
            root = addNode;
            return;
        }
        root.add(addNode);

        // 如果右子树的高度 - 左子树的高度 > 1 进行左旋转
        if (this.root.getRightHeight() - this.root.getLeftHeight() > 1) {
            // 如果 右子树的左节点高度 > 右子树的右节点高度 那么先进行右旋转
            if (this.root.getRight().getLeftHeight() > this.root.getRight().getRightHeight()) {
                this.root.getRight().rightRotate();
            }
            this.root.leftRotate();
            return;
        }
        // 如果左子树的高度 - 右子树的高度 > 1 进行右旋转
        if (this.root.getLeftHeight() - this.root.getRightHeight() > 1) {
            // 如果左子树的右节点的高度 > 左子树的左节点的高度 那么要先进行左旋转
            if (this.root.getLeft().getRightHeight() > this.root.getLeft().getLeftHeight()) {
                this.root.getLeft().leftRotate();
            }
            // 进行右旋转
            this.root.rightRotate();
        }
    }

    public void infixOrder() {
        this.infixOrder(this.root);
    }

    private void infixOrder(Node node) {
        if (node.getLeft() != null) {
            infixOrder(node.getLeft());
        }
        System.out.println(node);
        if (node.getRight() != null) {
            infixOrder(node.getRight());
        }
    }

    public Node getRoot() {
        return root;
    }

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

class Node {

    private int value;
    private Node left;
    private Node right;

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

    /**
      * 左旋转
      */
    public void leftRotate() {
        // 创建一个新节点值 = 当前节点
        Node newNode = new Node(this.value);
        // 新节点的左节点 = 当前节点的左节点
        newNode.setLeft(this.left);
        // 新节点的右节点 = 当前节点的右节点的左节点
        newNode.setRight(this.right.getLeft());
        // 当前节点的左节点 = 新节点
        this.left = newNode;
        // 当前节点的值 = 右节点的值
        this.value = this.right.getValue();
        // 当前节点的右节点 = 当前节点的右节点的右节点
        this.right = this.right.getRight();
    }

    /**
      * 右旋转
      */
    public void rightRotate() {
        // 创建一个 newNode 值为当前节点
        Node newNode = new Node(this.value);
        // newNode 的右节点 = 当前节点的右节点
        newNode.setRight(this.right);
        // newNode 的左节点 = 当前节点的左节点的右节点
        newNode.setLeft(this.left.getRight());
        // 当前节点的右节点 = newNode
        this.right = newNode;
        // 当前节点的值 = 当前节点的左节点的值
        this.value = this.left.getValue();
        // 当前节点的左节点 = 当前节点的左节点的左节点
        this.left = this.left.getLeft();
    }

    public void add(Node addNode) {

        if (this.value > addNode.getValue()) {

            // 如果左边值 null 直接添加
            if (this.left == null) {
                left = addNode;
                return;
            }
            // 否则向左递归
            this.left.add(addNode);
        } else {

            // 如果右边值 null 直接添加
            if (this.right == null) {
                this.right = addNode;
                return;
            }
            // 否则向右递归
            this.right.add(addNode);
        }
    }

    public int getLeftHeight() {
        return this.getLeft() == null ? 0 : this.getLeft().getHeight();
    }

    public int getRightHeight() {
        return this.getRight() == null ? 0 : this.getRight().getHeight();
    }


    public int getHeight() {
        int height = Math.max(this.left == null ? 0 : this.left.getHeight(), this.right == null ? 0 : this.right.getHeight()) + 1;
        return height;
    }

    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;
    }

    @Override
    public String toString() {
        return "Node{" +
            "value=" + value +
            '}';
    }
}