平衡二叉树—AVL树

在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

特点

AVL树本质上还是一颗二叉查找树,具有以下特点:

  • 是一颗二叉查找树
  • 每个节点的左右子树的高度之差的绝对值(平衡因子)最多为1。

下图就是一个AVL树,每个节点旁边标注的是平衡因子:

其中节点4的左子树高度为0,右子树不存在,所以平衡因子是0 - (-1) = 1;所有的叶子节点不存在左右子树,所以平衡因子是0。

对于上面的AVL树,如果插入0,那么此时的二叉树为:

节点4的平衡因子为2,打破了AVL树的平衡。那么如何恢复AVL的平衡呢?只需要左旋转和右旋转这两种操作就能完成。

旋转

对于AVL树的旋转分为四种情况,分别是左左型、右右型、左右型、右左型。

左左型

对于图中节点都偏向于左边的情况,称为左左型,这个时候我们对节点4进行右旋操作,使它恢复平衡。

右旋:即顺时针旋转两个节点,使父节点4被自己的左子节点1取代,而节点4成为节点1的右子节点。

在举一个例子:

节点6的平衡因子为2,此时是左左型,进行右旋操作。此时节点4的右子节点成为了节点6的左子节点

右右型


图中为右右型,进行左旋操作

右左型


图中为右左型,先对节点6进行右旋操作,在对节点4进行左旋操作

左右型


图中为左右型,先对节点2进行左旋操作,在对节点8进行右旋操作。

代码实现

public class Node {
    int data;
    Node lchild;
    Node rchild;
    int height;

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

public class AVLTree {
    private Node root;

    // 记录节点的高度
    private int height(Node node) {
        if (node == null) {
            return -1;
        } else {
            return node.height;
        }
    }

    // 右右型 左旋
    private Node L_Rotate(Node node) {
        Node temp;

        // 进行旋转
        temp = node.rchild;
        node.rchild = temp.lchild;
        temp.lchild = node;

        // 重新计算高度
        node.height = Math.max(height(node.lchild), height(node.rchild)) + 1;
        temp.height = Math.max(height(temp.lchild), height(temp.rchild)) + 1;

        return temp;
    }

    // 左左型 右旋
    private Node R_Rotate(Node node) {
        Node temp;

        // 进行旋转
        temp = node.lchild;
        node.lchild = temp.rchild;
        temp.rchild = node;

        // 重新计算高度
        node.height = Math.max(height(node.lchild), height(node.rchild)) + 1;
        temp.height = Math.max(height(temp.lchild), height(temp.rchild)) + 1;

        return temp;
    }

    // 左右型 先进行左旋在进行右旋
    private Node L_R_Rotate(Node node) {
        // 对左子节点进行左旋
        node.lchild = L_Rotate(node.lchild);

        // 对节点进行右旋
        return R_Rotate(node);
    }

    // 右左型 先进性右旋在进行左旋
    private Node R_L_Rotate(Node node) {
        // 对右子节点进行右旋
        node.rchild = R_Rotate(node.rchild);

        // 对节点进行左旋
        return L_Rotate(node);
    }

    public void insert(int data) {
        root = insert(data, root);
    }

    // 插入操作
    private Node insert(int data, Node node) {
        if (node == null) {
            node = new Node(data);
        } else if (data < node.data) {
            // 向左子节点递归插入
            node.lchild = insert(data, node.lchild);

            // 如果左子节点的高度比右子节点的高度大2 则进行旋转调整
            if (height(node.lchild) - height(node.rchild) == 2){
                if (data < node.lchild.data) {  // 左左型
                    node = R_Rotate(node);
                } else {   // 左右型
                    node = R_L_Rotate(node);
                }
            }
        } else if (data > node.data) {
            // 向右子节点递归插入
            node.rchild = insert(data, node.rchild);

            // 如果右子节点的高度比左子节点的高度大2 则进行旋转调整
            if (height(node.rchild) - height(node.lchild) == 2) {
                if (data > node.rchild.data) { // 右右型
                    node = L_Rotate(node);
                } else {  // 右左型
                    node = R_L_Rotate(node);
                }
            }
        }
        // 如果 data = node.data 这个节点在树上存在 什么也不做

        node.height = Math.max(height(node.lchild), height(node.rchild)) + 1;
        return node;
    }

    // 中序遍历AVL树
    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(Node node) {
        if (node != null) {
            inOrder(node.lchild);
            System.out.println(node.data);
            inOrder(node.rchild);
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值