在计算机科学中,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);
}
}
}