平衡二叉树

平衡二叉树

  平衡二叉树: 平衡二叉树也叫平衡二叉搜索树 (Self-balancing Binary Search Tree) 又被称为 AVL 树,可以保证查询效率较高。平衡二叉树它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。

左旋转

举例说明,下面这颗二叉树的左子树的深度为 1 ,右子树的深度为 3, 相差的绝对值超过了 1 ,通过左旋转达到平衡。
在这里插入图片描述
步骤:

  1. 创建新的结点(newNode),新结点的 value 是当前结点的 value

  2. 把新结点的左子结点设置为当前结点的左子结点

  3. 把新结点的右子结点设置为当前结点的右子树的左子结点
    平衡二叉树02

  4. 把当前结点的 value 设置为当前结点的右子结点的 value
    在这里插入图片描述

  5. 当前结点的右子结点指向当前结点的右子结点的右子结点

  6. 当前结点的左子结点指向新的结点
    在这里插入图片描述

之前的结点 4 就没有结点指向它,回收机制回收后平衡二叉树就调整完成,如下
在这里插入图片描述
代码

public void leftRotation() {
    // 如果需要双旋转,即右子树的的 左子树的深度 > 右子树的深度,就需要先将右子树右旋转
    if (right.leftHeight()  > right.rightHeight()) {
        right.rightRotation();
    }
    // 创建新的结点,新结点的 value 是当前结点的 value
    Node node = new Node(value);
    // 把新结点的左子结点设置为当前结点的左子结点
    node.left = left;
    // 把新结点的右子结点设置为当前结点的右子树的左子结点
    node.right = right.left;
    // 把当前结点的 value 设置为当前结点的右子结点的 value
    value = right.value;
    // 当前结点的右子结点指向当前结点的右子结点的右子结点
    right = right.right;
    // 当前结点的左子结点指向新的结点
    left = node;
}

右旋转

举例说明,下面这颗二叉树的右子树的深度为 1 ,左子树的深度为 3, 相差的绝对值超过了 1 ,通过右旋转达到平衡。

在这里插入图片描述

步骤(和左旋转步骤对称,图就省略):

  1. 创建新的结点, value 是当前结点的 value
  2. 把新的结点的右子结点设置为当前结点的右子结点
  3. 把新的结点的左子结点设置为当前结点的左子树的右子结点
  4. 把当前的结点的 value 设置为当前结点的左子结点的 value
  5. 把当前结点的左子结点指向当前结点的左子结点的左子结点
  6. 把当前结点的右子结点指向新的结点

代码

public void rightRotation() {
    // 如果需要双旋转,即左子树的的 右子树的深度 > 左子树的深度,就需要先将左子树左旋转
    if (left.rightHeight()  > left.leftHeight()) {
        left.leftRotation();
    }
    // 创建新的结点, value 是当前结点的 value
    Node node = new Node(value);
    // 把新的结点的右子结点设置为当前结点的右子结点
    node.right = right;
    // 把新的结点的左子结点设置为当前结点的左子树的右子结点
    node.left = left.right;
    // 把当前的结点的 value 设置为当前结点的左子结点的 value
    value = left.value;
    // 把当前结点的左子结点指向当前结点的左子结点的左子结点
    left = left.left;
    // 把当前结点的右子结点指向新的结点
    right = node;
}

双旋转

如果遇到下面这两种情况

  1. 左子树的的 右子树的深度 > 左子树的深度,就需要先将左子树左旋转
    在这里插入图片描述

  2. 右子树的的 左子树的深度 > 右子树的深度,就需要先将右子树右旋转
    在这里插入图片描述

完整代码

下面代码在二叉排序树的基础上添加平衡树的方法

public class AVLTreeDemo {
    public static void main(String[] args) {
        int[] arr = { 10, 11, 7, 6, 8, 9 };
        AVLTree avlTree = new AVLTree();
        for(int i = 0; i < arr.length; i++) {
            avlTree.add(new Node(arr[i]));
        }
        /* 再添加最后元素 9 时,该二叉平衡树转化如下(10 的左子树先左旋转, 以 10 为根节点的树再右旋转)
                        10                                10                                8
                   *         *                       *        *                        *        *
                7              11                  8             11                 7             10
              *   *                   =>         *   *                    =>       *            *    *
            6       8                          7       9                         6            9        11
                     *                       *
                      9                     6
        */
        System.out.println("-------------------中序遍历--------------------");
        avlTree.infixOrder();
        System.out.println();

        System.out.println("根节点:" + avlTree.root);

        System.out.println("8 left-->   " + avlTree.root.left);
        System.out.println("8 right-->  " + avlTree.root.right);

        System.out.println("7 left-->   " + avlTree.root.left.left);

        System.out.println("10 left-->  " + avlTree.root.right.left);
        System.out.println("10 right--> " + avlTree.root.right.right);
    }
}

class AVLTree {
    Node root;

    // 向平衡二叉树中中添加结点法
    public void add(Node node) {
        if (root != null) {
            root.add(node);
        } else {
            root = node;
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (root != null) {
            root.infixOrder();
        } else {
            System.out.println("空树");
        }
    }
}

class Node {
    int value;
    Node left;
    Node right;

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

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

    // 该结点为根节点的树的高度
    public int height() {
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    }

    // 左子树的深度
    public int leftHeight() {
        if (left == null) {
            return 0;
        } else {
            return left.height();
        }
    }

    // 右子树的深度
    public int rightHeight() {
        if (right == null) {
            return 0;
        } else {
            return right.height();
        }
    }

    // 左旋转的方法
    public void leftRotation() {
        // 如果需要双旋转,即右子树的的 左子树的深度 > 右子树的深度,就需要先将右子树右旋转
        if (right.leftHeight()  > right.rightHeight()) {
            right.rightRotation();
        }
        // 创建新的结点,新结点的 value 是当前结点的 value
        Node node = new Node(value);
        // 把新结点的左子结点设置为当前结点的左子结点
        node.left = left;
        // 把新结点的右子结点设置为当前结点的右子树的左子结点
        node.right = right.left;
        // 把当前结点的 value 设置为当前结点的右子结点的 value
        value = right.value;
        // 当前结点的右子结点指向当前结点的右子结点的右子结点
        right = right.right;
        // 当前结点的左子结点指向新的结点
        left = node;
    }

    // 右旋转的方法
    public void rightRotation() {
        // 如果需要双旋转,即左子树的的 右子树的深度 > 左子树的深度,就需要先将左子树左旋转
        if (left.rightHeight()  > left.leftHeight()) {
            left.leftRotation();
        }
        // 创建新的结点, value 是当前结点的 value
        Node node = new Node(value);
        // 把新的结点的右子结点设置为当前结点的右子结点
        node.right = right;
        // 把新的结点的左子结点设置为当前结点的左子树的右子结点
        node.left = left.right;
        // 把当前的结点的 value 设置为当前结点的左子结点的 value
        value = left.value;
        // 把当前结点的左子结点指向当前结点的左子结点的左子结点
        left = left.left;
        // 把当前结点的右子结点指向新的结点
        right = node;
    }

    // 向平衡二叉树中添加结点
    public void add(Node node) {
        if (node == null) {
            return;
        }
        // 添加的结点小于当前结点
        if (node.value < this.value) {
            if (this.left == null) {
                this.left = node;
            } else { // 向左边递归添加
                this.left.add(node);
            }
        } else { // 添加的结点大于当前结点
            if (this.right == null) {
                this.right = node;
            } else { // 向右边递归添加
                this.right.add(node);
            }
        }
        // 如果该树的 右子树的高度 - 左子树的高度 > 1 时,需要左旋转达到平衡
        if (rightHeight() - leftHeight() > 1) {
            leftRotation();
        } else if (leftHeight() - rightHeight() > 1) { // 如果该树的 左子树的高度 - 右子树的高度 > 1 时,需要右旋转达到平衡
            rightRotation();
        }
    }

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

运行结果

-------------------中序遍历--------------------
6    7    8    9    10    11    
根节点:8    
8 left-->   7    
8 right-->  10    
7 left-->   6    
10 left-->  9    
10 right--> 11 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值