AVL树是一种自平衡二叉搜索树,其中任何节点的两个子树的高度最多相差1。它以发明者Adelson-Velsky和Landis的名字命名。当进行插入或删除操作后,如果某个节点的平衡因子(左子树高度减去右子树高度的差)不是-1、0或1,则需要通过旋转来重新平衡树。
下面是创建和维护AVL树的一些核心实践步骤:
旋转操作
为了维持AVL树的平衡性,在插入或删除节点时可能需要执行以下四种旋转操作之一:
- 右旋(Right Rotation) - 当节点的左子树比右子树高时:
T1, T2, T3 and T4 are subtrees. z y / \ / \ y T4 Right Rotate (z) x z / \ - - - - - - - - -> / \ / \ x T3 T1 T2 T3 T4 / \
T1 T2
```
-
左旋(Left Rotation) - 当节点的右子树比左子树高时:
z y / \ / \ T1 y Left Rotate(z) z x / \ - - - - - - - -> / \ / \ T2 x T1 T2 T3 T4 / \ T3 T4
-
左-右旋(Left-Right Rotation) - 先对节点的左子树进行左旋,再对节点进行右旋。
-
右-左旋(Right-Left Rotation) - 先对节点的右子树进行右旋,再对节点进行左旋。
插入操作
插入操作首先类似于二叉搜索树的插入,之后包括以下步骤:
- 更新每个节点的高度。
- 计算每个节点的平衡因子(即左子树高度 - 右子树高度)。
- 如果平衡因子不是-1、0或1,则根据子树的高度差进行适当的旋转操作以重新平衡。
删除操作
删除操作首先与二叉搜索树的删除类似,之后也包括类似插入操作的平衡调整:
- 如果删除了一个节点,需要回溯到根节点,并更新沿途节点的高度。
- 计算每个节点的平衡因子。
- 如果平衡因子不是-1、0或1,执行适当的旋转操作。
AVL树代码实现
下面是创建AVL树节点和进行右旋转操作的Java代码片段示例:
class AVLNode {
int key, height;
AVLNode left, right;
public AVLNode(int d) {
key = d;
height = 1; // 新节点初始高度设置为1
}
}
class AVLTree {
AVLNode root;
// 获取节点的高度
int height(AVLNode N) {
if (N == null)
return 0;
return N.height;
}
// 右旋转操作
AVLNode rightRotate(AVLNode y) {
AVLNode x = y.left;
AVLNode T2 = x.right;
// 旋转
x.right = y;
y.left = T2;
// 更新高度
y.height = Math.max(height(y.left), height(y.right)) + 1;
x.height = Math.max(height(x.left), height(x.right)) + 1;
// 返回新的根节点
return x;
}
// TODO: 左旋转、左-右旋转、右-左旋转、插入和删除操作的实现
}
// 使用
public class Main {
public static void main(String[]```java
args) {
AVLTree tree = new AVLTree();
// 示例插入操作
tree.root = tree.insert(tree.root, 10);
tree.root = tree.insert(tree.root, 20);
tree.root = tree.insert(tree.root, 30);
tree.root = tree.insert(tree.root, 40);
tree.root = tree.insert(tree.root, 50);
tree.root = tree.insert(tree.root, 25);
// AVL树现在应该平衡了
// 打印树的中序遍历结果
tree.inOrder(tree.root);
}
}
class AVLTree {
// ...(之前的代码)
// 左旋转操作
AVLNode leftRotate(AVLNode x) {
AVLNode y = x.right;
AVLNode T2 = y.left;
// 旋转
y.left = x;
x.right = T2;
// 更新高度
x.height = Math.max(height(x.left), height(x.right)) + 1;
y.height = Math.max(height(y.left), height(y.right)) + 1;
// 返回新的根节点
return y;
}
// 获取平衡因子
int getBalance(AVLNode N) {
if (N == null)
return 0;
return height(N.left) - height(N.right);
}
// 插入节点操作
AVLNode insert(AVLNode node, int key) {
// 执行BST的常规插入
if (node == null)
return (new AVLNode(key));
if (key < node.key)
node.left = insert(node.left, key);
else if (key > node.key)
node.right = insert(node.right, key);
else // 不允许有重复键
return node;
// 更新父节点的高度
node.height = 1 + Math.max(height(node.left), height(node.right));
// 获取平衡因子,检查是否失衡
int balance = getBalance(node);
// 如果节点失衡,则进行4种情况的检查并旋转
// 左左情况
if (balance > 1 && key < node.left.key)
return rightRotate(node);
// 右右情况
if (balance < -1 && key > node.right.key)
return leftRotate(node);
// 左右情况
if (balance > 1 && key > node.left.key) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// 右左情况
if (balance < -1 && key < node.right.key) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
// 返回未失衡的节点
return node;
}
void inOrder(AVLNode node) {
if (node != null) {
inOrder(node.left);
System.out.print(node.key + " ");
inOrder(node.right);
}
}
}
在上述代码中,insert
方法实现了AVL树的插入操作,它首先执行标准的二叉搜索树插入过程,然后更新节点的高度,并通过getBalance
方法获取平衡因子来检查树是否失衡。如果树失衡,根据失衡类型执行相应的旋转操作。我们还实现了中序遍历的递归方法inOrder
来打印树的内容。
请注意,这是一个简化的AVL树实现,仅用于演示目的。在实际应用中,你可能需要更完整的实现,例如包含删除节点、错误处理和更健壮的数据输入处理。此外,上述代码只在Java中演示了概念,并没有涵盖所有的边缘情况和异常处理。