《数据结构与算法之二叉平衡树(AVL)》

说在前头:本人为大二在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正。若在阅读时有任何的问题,也可通过评论提出,本人将根据自身能力对问题进行一定的解答。

前言

在上一篇文章《数据结构与算法之二叉搜索树(Java实现)》中,我们探讨了二叉搜索树,并在其中指出了二叉搜索树的弊端:在数据有序时,二叉搜索树的结构会退化成链表结构。今天我们就来探讨为解决该弊端而出现的数据结构——二叉平衡树AVL树。

01—什么是AVL树

AVL树的命名取自该结构的两位发明者(Adelson-Velsky和E.M. Landis)名字的缩写。

AVL树继承了二叉搜索树的基本特征:左子节点小于父节点,父节点小于等于右子节点,但与二叉搜索树不同的是,AVL树增加了一个新的特性:任何节点的两颗子树的高度相差最多只可以为一层!

02—标准的AVL树图示

下图为一颗标准的AVL树:

下图是不符合AVL规则的树:

03—旋转与其对应的四种情况

了解了AVL的规则后,我们需要开始思考:当插入的数据会破坏AVL高度不能相差超过一层的规则时,要进行怎样的操作才可以使得结构重新遵循规则。为了保证规则的遵循,我们需要引入旋转的思想!

当AVL规则被破坏失去平衡时,可以分为四种情况:LL(左左)、LR(左右)、RR(右右)、RL(右左)。

LL(左左)情况:

LR(左右)情况:

RR(右右)情况:

RL(右左)情况:

04—LL的旋转

LL旋转示意图:

LL旋转:找到破环规则的那一个节点的父节点,并对其父节点与爷节点进行父子位的交换,对应的子节点也需要进行替换。如上图:找到破环规则的节点的父节点x1,需要对x1父节点node2与爷节点node1进行父子位进行互换,在互换的过程中,因为node2需要断开原来的的右子节点y与node1连接,node1的左子节点断开了node2的连接,此时出现node1左子节点为空,因此,node2原来的右子节点需要与node1的左子节点y连接。

05—RR的旋转

RR旋转:RR旋转与LL旋转的思想是一致的,找到破环规则的那一个节点的父节点,并对其父节点与爷节点进行父子位的交换,对应的子节点也需要进行替换。如上图:找到破环规则的节点的父节点x1,需要对x1父节点node2与爷节点node1进行父子位进行互换,在互换的过程中,因为node2需要断开原来的的左子节点y与node1连接,node1的右子节点断开了node2的连接,此时出现node1右子节点为空,因此,node2原来的右子节点需要与node1的左子节点y连接。

06—LR的旋转

LR旋转:LR旋转相对LL和RR比较复杂,LR需要旋转两次,分别是旋转一次RR再旋转一次LL。如上图:先围绕node2做RR旋转后再围绕node1做LL旋转。

07—RL的旋转

RL旋转:RL旋转相对RR和LL比较复杂,RL需要旋转两次,分别是旋转一次LL再旋转一次RR。如上图:先围绕node2做LL旋转后再围绕node1做RR旋转。

至此,我们讲述和演示完了AVL四种旋转情况,接下来我们将用代码来实现具体的功能。

08—具体代码

树的节点类Node.java

package com.bosen.www;
​
/**
 * <p>节点类</p>
 * @author Bosen 2021/6/10 22:01
 */
public class Node {
    public Node left;   // 左节点
    public int data;    // 数据
    public Node right;  // 右节点
​
    public Node(Node left, int data, Node right) {
        this.left = left;
        this.data = data;
        this.right = right;
    }
}

AVL树实现类AVLTree.java

package com.bosen.www;
​
/**
 * <p>AVL树</p>
 * @author Bosen 2021/6/10 22:07
 */
public class AVLTree {
    public Node root; // 根节点
​
    /*
     * 插入操作(递归实现)
     */
    public void insert(int data) {
        root = insert(root, data);
    }
    public Node insert(Node subNode, int data) {
        if (subNode == null) {
            return new Node(null, data, null);
        }
        if (data < subNode.data) {
            // 插入到左子树
            subNode.left = insert(subNode.left, data);
            if (unbalance(subNode)) {
                // 规则被破坏
                if (data < subNode.left.data) {
                    subNode = ll(subNode); // LL
                } else {
                    subNode = lr(subNode); // LR
                }
            }
        } else {
            // 插入到右子树
            subNode.right = insert(subNode.right, data);
            if(unbalance(subNode)) {
                // 规则被破坏
                if (data < subNode.right.data) {
                    subNode = rr(subNode); // RR
                } else {
                    subNode = rl(subNode); // RL
                }
            }
        }
        return subNode;
    }
    
    /*
     * 删除操作
     */
    public void delete(int data) {
        root = delete(root, data);
    }
    public Node delete(Node subNode, int data) {
        if (data < subNode.data) {
            // 需要删除的节点在左子树
            subNode.left = delete(subNode.left, data);
        } else if (data > subNode.data){
            // 需要删除的节点在右子树
            subNode.right = delete(subNode.right, data);
        } else {
            // 此节点为需要删除的节点
            if (subNode.left != null && subNode.right != null) {
                // 左右子树都存在
                if (getDepth(subNode.left, 1) > getDepth(subNode.right, 1)) {
                    // 左子树高度高于右子树
                    Node max = getMaxNode(subNode.left);
                    subNode.data = max.data;
                    subNode.left = delete(subNode.left, max.data);
                } else {
                    // 右子树高度高于左子树
                    Node min = getMaxNode(subNode.right);
                    subNode.data = min.data;
                    subNode.right = delete(subNode.right, min.data);
                }
            } else {
                subNode = subNode.left != null ? subNode.left : subNode.right;
            }
        }
        return subNode;
    }
​
    /*
     * LL
     */
    public Node ll(Node node1) {
        Node node2 = node1.left;
        node1.left = node2.right;
        node2.right = node1;
        return node2;
    }
​
    /*
     * RR
     */
    public Node rr(Node node1) {
        Node node2 = node1.right;
        node1.right = node2.left;
        node2.left = node1;
        return node2;
    }
​
    /*
     * Lr
     */
    public Node lr(Node node1) {
        node1.left = rr(node1.left);
        return ll(node1);
    }
​
    /*
     * rl
     */
    public Node rl(Node node1) {
        node1.right = ll(node1.right);
        return rr(node1);
    }
​
    /*
     * 中序遍历
     */
    public void traverse(Node root) {
        if (root != null) {
            traverse(root.left);
            System.out.print(root.data+"\t");
            traverse(root.right);
        }
    }
​
    /*
     * 判断树的深度
     */
    public int getDepth(Node root, int deep) {
        if (root == null) {
            return deep;
        }
        int leftDeep = deep;
        int rightDeep = deep;
        if (root.left != null) {
            leftDeep = getDepth(root.left, deep+1);
        }
        if (root.right != null) {
            rightDeep = getDepth(root.right, deep+1);
        }
        return Math.max(leftDeep, rightDeep);
    }
​
    /*
     * 判断规则是否被破坏
     */
    public boolean unbalance(Node root) {
        int leftHeight = getDepth(root.left, 1);
        int rightHeight = getDepth(root.right, 1);
        return Math.abs(leftHeight - rightHeight) > 1;
    }
​
    /*
     * 获取最大节点
     */
    public Node getMaxNode(Node node) {
        if (node == null) {
            return null;
        }
        if (node.right != null) {
            return getMaxNode(node.right);
        } else {
            return node;
        }
    }
​
    /*
     * 获取最小节点
     */
    private Node getMinNode(Node node) {
        if (node == null) {
            return null;
        }
        if (node.left != null) {
            return getMinNode(node.left);
        } else {
            return node;
        }
    }
}

 👇扫描二维码关注

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云丶言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值