引言
平衡二叉树(AVL树)是一种特殊的二叉排序树,它通过保持树的平衡性来确保查询、插入和删除操作的高效性。与普通的二叉排序树相比,平衡二叉树能够避免树退化为链表的情况,从而保证操作的时间复杂度始终为 O(log n)
。本文将深入探讨平衡二叉树的基本概念、旋转操作以及实现方法,并通过代码示例和实际案例帮助读者全面掌握平衡二叉树的核心知识。
主体部分
1. 平衡二叉树的基本概念
1.1 什么是平衡二叉树?
平衡二叉树(AVL树)是一种自平衡的二叉排序树,具有以下特点:
-
平衡性:对于任意节点,其左右子树的高度差的绝对值不超过1。
-
递归性:左右子树本身也是平衡二叉树。
示例:
4 / \ 2 6 / \ / \ 1 3 5 7
1.2 为什么需要平衡二叉树?
普通的二叉排序树在极端情况下(如插入有序数据)会退化为链表,导致操作的时间复杂度从 O(log n)
退化为 O(n)
。平衡二叉树通过旋转操作保持树的平衡性,从而避免这种情况。
2. 平衡二叉树的旋转操作
平衡二叉树通过旋转操作来维持平衡性,主要包括以下四种情况:
-
左旋转:当右子树高度比左子树高度大超过1时。
-
右旋转:当左子树高度比右子树高度大超过1时。
-
左右旋转:先对左子树进行左旋转,再对根节点进行右旋转。
-
右左旋转:先对右子树进行右旋转,再对根节点进行左旋转。
2.1 左旋转
场景:
当右子树高度比左子树高度大超过1时,需要进行左旋转。
步骤:
-
创建一个新节点,值为当前根节点的值。
-
将新节点的左子树设置为当前节点的左子树。
-
将新节点的右子树设置为当前节点右子树的左子树。
-
将当前节点的值替换为右子节点的值。
-
将当前节点的右子树设置为右子树的右子树。
-
将当前节点的左子树设置为新节点。
代码实现:
private void leftRotate() {
Node newNode = new Node(value);
newNode.left = left;
newNode.right = right.left;
value = right.value;
right = right.right;
left = newNode;
}
2.2 右旋转
场景:
当左子树高度比右子树高度大超过1时,需要进行右旋转。
步骤:
-
创建一个新节点,值为当前根节点的值。
-
将新节点的右子树设置为当前节点的右子树。
-
将新节点的左子树设置为当前节点左子树的右子树。
-
将当前节点的值替换为左子节点的值。
-
将当前节点的左子树设置为左子树的左子树。
-
将当前节点的右子树设置为新节点。
代码实现:
private void rightRotate() {
Node newNode = new Node(value);
newNode.right = right;
newNode.left = left.right;
value = left.value;
left = left.left;
right = newNode;
}
2.3 左右旋转
场景:
当左子树的右子树高度大于左子树的左子树高度时,需要先对左子树进行左旋转,再对根节点进行右旋转。
代码实现:
if (leftHeight() - rightHeight() > 1) {
if (left != null && left.rightHeight() > left.leftHeight()) {
left.leftRotate();
}
rightRotate();
}
2.4 右左旋转
场景:
当右子树的左子树高度大于右子树的右子树高度时,需要先对右子树进行右旋转,再对根节点进行左旋转。
代码实现:
if (rightHeight() - leftHeight() > 1) {
if (right != null && right.leftHeight() > right.rightHeight()) {
right.rightRotate();
}
leftRotate();
}
3. 平衡二叉树的实现
3.1 节点类
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
// 返回左子树的高度
public int leftHeight() {
return left == null ? 0 : left.height();
}
// 返回右子树的高度
public int rightHeight() {
return right == null ? 0 : right.height();
}
// 返回以该节点为根的树的高度
public int height() {
return Math.max(leftHeight(), rightHeight()) + 1;
}
// 添加节点
public void add(Node node) {
if (node.value < this.value) {
if (this.left == null) {
this.left = node;
} else {
this.left.add(node);
}
} else {
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
// 平衡操作
if (rightHeight() - leftHeight() > 1) {
if (right != null && right.leftHeight() > right.rightHeight()) {
right.rightRotate();
}
leftRotate();
} else if (leftHeight() - rightHeight() > 1) {
if (left != null && left.rightHeight() > left.leftHeight()) {
left.leftRotate();
}
rightRotate();
}
}
// 中序遍历
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
}
3.2 平衡二叉树类
class AVLTree {
private Node root;
// 添加节点
public void add(Node node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
// 中序遍历
public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("二叉树为空");
}
}
}
3.3 测试代码
public class AVLTreeDemo {
public static void main(String[] args) {
int[] arr = {10, 11, 7, 6, 8, 9};
AVLTree avlTree = new AVLTree();
for (int i : arr) {
avlTree.add(new Node(i));
}
System.out.println("中序遍历:");
avlTree.infixOrder();
System.out.println("树的高度:" + avlTree.getRoot().height());
System.out.println("左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("右子树高度:" + avlTree.getRoot().rightHeight());
}
}
运行结果:
中序遍历:
6
7
8
9
10
11
树的高度:3
左子树高度:2
右子树高度:1
结论
平衡二叉树通过旋转操作保持树的平衡性,从而确保查询、插入和删除操作的高效性。通过本文的深入剖析和代码示例,读者可以掌握平衡二叉树的核心概念和实现方法。在实际应用中,平衡二叉树常用于数据库索引、文件系统等场景。
在接下来的文章中,我们将继续探讨更高级的树结构(如红黑树、B树等),敬请期待!
系列文章
如果你对平衡二叉树或其他树结构有任何疑问,欢迎在评论区留言讨论!