Java 数据结构 - 平衡树
Java 中的平衡树:AVL 树和红黑树
1. 引言
平衡树是一种特殊的二叉搜索树(BST),它通过某种机制保持树的平衡,从而确保主要操作(如插入、删除和查找)的时间复杂度保持在 O(log n)。本文将介绍两种常见的平衡树:AVL 树和红黑树,并使用 Java 语言实现它们的基本结构和操作。
2. AVL 树
2.1 AVL 树的定义
AVL 树是最早被发明的自平衡二叉搜索树。在 AVL 树中,任何节点的两个子树的高度最多相差1。这个性质保证了树的平衡,使得树的高度始终保持在 O(log n)。
2.2 Java 实现 AVL 树
定义 AVL 树的节点:
class AVLNode {
int val;
int height;
AVLNode left, right;
AVLNode(int d) {
val = d;
height = 1;
}
}
实现 AVL 树的主要方法:
class AVLTree {
private AVLNode root;
// 获取节点高度
private int height(AVLNode N) {
if (N == null)
return 0;
return N.height;
}
// 计算平衡因子
private int getBalance(AVLNode N) {
if (N == null)
return 0;
return height(N.left) - height(N.right);
}
// 右旋转
private 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;
}
// 左旋转
private 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;
}
// 插入节点
public void insert(int val) {
root = insert(root, val);
}
private AVLNode insert(AVLNode node, int val) {
if (node == null)
return (new AVLNode(val));
if (val < node.val)
node.left = insert(node.left, val);
else if (val > node.val)
node.right = insert(node.right, val);
else
return node;
node.height = 1 + Math.max(height(node.left), height(node.right));
int balance = getBalance(node);
// 左左情况
if (balance > 1 && val < node.left.val)
return rightRotate(node);
// 右情况
if (balance < -1 && val > node.right.val)
return leftRotate(node);
// 左右情况
if (balance > 1 && val > node.left.val) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// 右左情况
if (balance < -1 && val < node.right.val) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node;
}
}
2.3 AVL 树的特点
- 严格平衡,任何节点的左右子树高度差不超过1
- 查找、插入和删除操作的时间复杂度都是 O(log n)
- 因为需要保持严格的平衡,插入和删除操作可能需要多次旋转,开销较大
3. 红黑树
3.1 红黑树的定义
红黑树是一种近似平衡的二叉搜索树,它通过节点的着色和一系列规则来保持平衡。红黑树的每个节点都有一个颜色属性,可以是红色或黑色。通过保证从根到叶子的最长路径不多于最短路径的两倍,红黑树在最坏情况下仍能保持较好的平衡。
3.2 Java 实现红黑树
定义红黑树的节点:
class RBNode {
int val;
RBNode left, right, parent;
boolean isRed;
RBNode(int val) {
this.val = val;
this.isRed = true; // 新插入的节点默认为红色
}
}
实现红黑树的主要方法:
class RedBlackTree {
private RBNode root;
private RBNode NIL;
public RedBlackTree() {
NIL = new RBNode(0);
NIL.isRed = false;
root = NIL;
}
// 左旋转
private void leftRotate(RBNode x) {
RBNode y = x.right;
x.right = y.left;
if (y.left != NIL) {
y.left.parent = x;
}
y.parent = x.parent;
if (x.parent == null) {
root = y;
} else if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
y.left = x;
x.parent = y;
}
// 右旋转
private void rightRotate(RBNode y) {
RBNode x = y.left;
y.left = x.right;
if (x.right != NIL) {
x.right.parent = y;
}
x.parent = y.parent;
if (y.parent == null) {
root = x;
} else if (y == y.parent.right) {
y.parent.right = x;
} else {
y.parent.left = x;
}
x.right = y;
y.parent = x;
}
// 插入节点
public void insert(int val) {
RBNode node = new RBNode(val);
RBNode y = null;
RBNode x = root;
while (x != NIL) {
y = x;
if (node.val < x.val) {
x = x.left;
} else {
x = x.right;
}
}
node.parent = y;
if (y == null) {
root = node;
} else if (node.val < y.val) {
y.left = node;
} else {
y.right = node;
}
node.left = NIL;
node.right = NIL;
node.isRed = true;
insertFixup(node);
}
// 插入后修复红黑树性质
private void insertFixup(RBNode k) {
while (k.parent != null && k.parent.isRed) {
if (k.parent == k.parent.parent.left) {
RBNode u = k.parent.parent.right;
if (u.isRed) {
k.parent.isRed = false;
u.isRed = false;
k.parent.parent.isRed = true;
k = k.parent.parent;
} else {
if (k == k.parent.right) {
k = k.parent;
leftRotate(k);
}
k.parent.isRed = false;
k.parent.parent.isRed = true;
rightRotate(k.parent.parent);
}
} else {
RBNode u = k.parent.parent.left;
if (u.isRed) {
k.parent.isRed = false;
u.isRed = false;
k.parent.parent.isRed = true;
k = k.parent.parent;
} else {
if (k == k.parent.left) {
k = k.parent;
rightRotate(k);
}
k.parent.isRed = false;
k.parent.parent.isRed = true;
leftRotate(k.parent.parent);
}
}
if (k == root) {
break;
}
}
root.isRed = false;
}
}
3.3 红黑树的特点
- 每个节点要么是红色,要么是黑色
- 根节点是黑色
- 每个叶子节点(NIL)是黑色
- 如果一个节点是红色的,则它的子节点必须是黑色的
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点
- 插入和删除操作的平均时间复杂度为 O(log n)
- 相比 AVL 树,红黑树的插入和删除操作需要的旋转次数更少,因此在频繁插入删除的场景下性能更好
4. AVL 树与红黑树的比较
特性 | AVL 树 | 红黑树 |
---|---|---|
平衡条件 | 任何节点的两个子树的高度最多相差1 | 从根到叶子的最长路径不超过最短路径的两倍 |
节点存储开销 | 每个节点存储高度或平衡因子 | 每个节点存储颜色信息(1 bit) |
树高 | 更严格平衡,树高略低 | 相对较高,但最坏情况下仍是 O(log n) |
插入/删除时旋转次数 | 最多 O(log n) 次 | 最多3次(插入),最多2次(删除) |
适用场景 | 查找密集型任务 | 插入/删除密集型任务 |
5. Java 中的应用
在 Java 标准库中,TreeMap 和 TreeSet 都是基于红黑树实现的。这些数据结构提供了近似 O(log n) 的时间复杂度用于添加、删除和查找操作。
例如,使用 TreeSet:
TreeSet<Integer> set = new TreeSet<>();
set.add(5);
set.add(2);
set.add(7);
System.out.println(set); // 输出: [2, 5, 7]
6. 总结
AVL 树和红黑树都是重要的平衡树数据结构,它们通过不同的机制来维持树的平衡,从而保证了主要操作的高效性。AVL 树保持了更严格的平衡,适合于读取密集型的应用;而红黑树在插入和删除操作上的性能更好,适合于写入密集型的应用。在实际应用中,红黑树因其在插入和删除时需要的调整较少,且仍能保持良好的平衡,因此更为广泛地被采用,如在 Java 的集合框架中的应用。理解这两种树结构的原理和实现对于深入学习高级数据结构和算法至关重要。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix