说在前头:本人为大二在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正。若在阅读时有任何的问题,也可通过评论提出,本人将根据自身能力对问题进行一定的解答。
前言
在上一篇文章《数据结构与算法之二叉搜索树(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;
}
}
}
👇扫描二维码关注