数据结构-11-平衡二叉树

十一、平衡二叉树(AVL Tree)

1、概念

先看一个问题,如果给二叉排序树添加节点时是顺序的,那么创建出来的二叉排序树可能是一个单向链表,一直左子树或一直右子树,这样就失去了二叉树的优势(父节点可以含有两个子节点),而且树的高度可能会很大,查询效率会大大降低。

1                9
 2              8
  3            7
   4          6
    5        5

为了避免这种现象,产生了平衡二叉树。

又叫平衡二叉搜索树(Self-balancing binary search tree),特点是它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

常用的有:AVL Tree、红黑树、Treap Tree、替罪羊树和伸展树等。

2、方法

为了保证子树的高度差的绝对值不超过1,需要在给二叉树添加节点的时候,判断子树的高度差,一旦超过1,就需要通过左旋转或右旋转,来平衡左右子树的高度。

哪边子树高度较低,就向哪边旋转。

左旋转:

  1. 创建一个临时节点,节点的值是父节点的值。
  2. 将临时节点的左子节点指向父节点的左子节点。
  3. 将临时节点的右子节点指向父节点的右子节点的左子节点。
  4. 将父节点的值设置为右子节点的值。
  5. 将父节点的右子节点指向父节点的右子节点的右子节点。
  6. 将父节点的左子节点指向临时节点。

右旋转:

  1. 创建一个临时节点,节点的值是父节点的值。
  2. 将临时节点的右子节点指向父节点的右子节点。
  3. 将临时节点的左子节点指向父节点的左子节点的右子节点。
  4. 将父节点的值设置为左子节点的值。
  5. 将父节点的左子节点指向父节点的左子节点的左子节点。
  6. 将父节点的右子节点指向临时节点。

注意:

  1. 进行过左旋转后,就不要再进行右旋转。反之也是。
  2. 有时单纯地左旋转或右旋转,并不行保证树的结构是平衡二叉树,这就需要在左旋转之前先判断右子树的两个子树高度,一旦右子树的两个子树的高度不一致,则需要先对右子树进行右旋转。同样地,在右旋转之前,也需要判断左子树的两个子树的高度,是否一致,如果不一致,则需要先对左子树进行左旋转

3、示例

class AvlTree<T extends Comparable<T>> {

    private TreeNode<T> root;

    public void addTreeNode(TreeNode<T> node) {
        if (null == root) {
            root = node;
            return;
        }
        root.addNode(node);
    }

    public void infixOrder() {
        Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
        TreeNode<T> node = root;
        while (!stack.isEmpty() || null != node) {
            if (null != node) {
                stack.push(node);
                node = node.getLeft();
            } else {
                node = stack.pop();
                System.out.print(node.getData() + " ");
                node = node.getRight();
            }
        }
        System.out.println();
    }

    public TreeNode<T> getRoot() {
        return root;
    }

}

class TreeNode<T extends Comparable<T>> {

    private T data;

    private TreeNode<T> left;

    private TreeNode<T> right;

    public TreeNode(T data) {
        this.data = data;
    }

    public void addNode(TreeNode<T> node) {
        if (0 < this.data.compareTo(node.data)) {
            if (null == this.left) {
                this.left = node;
                return;
            }
            this.left.addNode(node);
        } else {
            if (null == this.right) {
                this.right = node;
                return;
            }
            this.right.addNode(node);
        }

        // 右子树的高度和左子树的高度相差超过1,进行左旋转
        if (1 < (this.rightHeight() - this.leftHeight())) {
            // 存在右子树,且右子树的两个子树高度不一致
            if (null != right && right.rightHeight() < right.leftHeight()) {
                right.rightRotate(); // 需要先对右子树进行右旋转
            }
            this.leftRotate();
            // 返回
            return;
        }

        // 左子树的高度和右子树的高度相差超过1,进行右旋转
        if (1 < (this.leftHeight() - this.rightHeight())) {
            // 存在左子树,且左子树的两个子树高度不一致
            if (null != left && left.leftHeight() < left.rightHeight()) {
                left.leftRotate(); // 需要先对左子树进行左旋转
            }
            this.rightRotate();
        }
    }

    /**
     * 节点子树的深度
     */
    public int height() {
        int leftHeight = 0;
        int rightHeight = 0;
        if (null != this.left) {
            leftHeight = this.left.height();
        }
        if (null != this.right) {
            rightHeight = this.right.height();
        }
        return ((leftHeight >= rightHeight) ? leftHeight : rightHeight) + 1;
    }

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

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

    /**
     * 节点左旋转
     */
    public void leftRotate() {
        TreeNode<T> tempNode = new TreeNode<T>(this.data);
        tempNode.left = this.left;
        tempNode.right = this.right.left;
        this.data = this.right.data;
        this.right = this.right.right;
        this.left = tempNode;
    }

    /**
     * 节点右旋转
     */
    public void rightRotate() {
        TreeNode<T> tempNode = new TreeNode<T>(this.data);
        tempNode.right = this.right;
        tempNode.left = this.left.right;
        this.data = this.left.data;
        this.left = this.left.left;
        this.right = tempNode;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public TreeNode<T> getLeft() {
        return left;
    }

    public void setLeft(TreeNode<T> left) {
        this.left = left;
    }

    public TreeNode<T> getRight() {
        return right;
    }

    public void setRight(TreeNode<T> right) {
        this.right = right;
    }

}


class Test {

    public static void main(String[] args) {
        // Integer[] array = { 1, 2, 3, 4, 5 };
        Integer[] array = { 2, 1, 6, 5, 7, 3 };
        AvlTree<Integer> avlTree = new AvlTree<Integer>();
        for (int i = 0, len = array.length; i < len; i++) {
            avlTree.addTreeNode(new TreeNode<Integer>(array[i]));
        }
        avlTree.infixOrder();

        TreeNode<Integer> root = avlTree.getRoot();
        System.out.println(root.height()); // 二叉树的深度
        System.out.println(root.getLeft().height()); // 左子树的深度
        System.out.println(root.getRight().height()); // 右子树的深度
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值