红黑树是一种自平衡的二叉查找树,它在每个节点上增加了一个额外的属性表示节点的颜色,可以是红色或黑色。通过满足一定的性质,红黑树确保了在进行插入、删除等操作时,树的高度保持在对数级别,从而保证了操作的时间复杂度为 O(log n)。
红黑树满足以下性质:
- 每个节点是红色或黑色。
- 根节点是黑色。
- 每个叶子节点(NIL节点,空节点)是黑色的。
- 如果一个节点是红色的,则它的两个子节点都是黑色的(不能有两个相连的红色节点)。
- 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点(黑色节点数量相同)。
这里是一个简单的红黑树的 Java 实现,包含插入操作:
java
class RedBlackTreeNode {
int data;
RedBlackTreeNode parent;
RedBlackTreeNode left;
RedBlackTreeNode right;
int color;
public RedBlackTreeNode(int data) {
this.data = data;
color = 1; // 新插入的节点默认为红色
parent = left = right = null;
}
}
public class RedBlackTree {
private RedBlackTreeNode root;
// 左旋
private void leftRotate(RedBlackTreeNode x) {
RedBlackTreeNode y = x.right;
x.right = y.left;
if (y.left != null)
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(RedBlackTreeNode y) {
RedBlackTreeNode x = y.left;
y.left = x.right;
if (x.right != null)
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 data) {
RedBlackTreeNode newNode = new RedBlackTreeNode(data);
RedBlackTreeNode parent = null;
RedBlackTreeNode current = root;
while (current != null) {
parent = current;
if (newNode.data < current.data)
current = current.left;
else
current = current.right;
}
newNode.parent = parent;
if (parent == null)
root = newNode;
else if (newNode.data < parent.data)
parent.left = newNode;
else
parent.right = newNode;
newNode.color = 1; // 新插入的节点默认为红色
fixInsert(newNode);
}
// 插入后修复红黑树性质
private void fixInsert(RedBlackTreeNode k) {
RedBlackTreeNode u;
while (k.parent.color == 1) {
if (k.parent == k.parent.parent.right) {
u = k.parent.parent.left;
if (u.color == 1) {
u.color = 0;
k.parent.color = 0;
k.parent.parent.color = 1;
k = k.parent.parent;
} else {
if (k == k.parent.left) {
k = k.parent;
rightRotate(k);
}
k.parent.color = 0;
k.parent.parent.color = 1;
leftRotate(k.parent.parent);
}
} else {
u = k.parent.parent.right;
if (u.color == 1) {
u.color = 0;
k.parent.color = 0;
k.parent.parent.color = 1;
k = k.parent.parent;
} else {
if (k == k.parent.right) {
k = k.parent;
leftRotate(k);
}
k.parent.color = 0;
k.parent.parent.color = 1;
rightRotate(k.parent.parent);
}
}
if (k == root) {
break;
}
}
root.color = 0;
}
// 中序遍历
private void inorder(RedBlackTreeNode root) {
if (root == null)
return;
inorder(root.left);
System.out.print(root.data + " ");
inorder(root.right);
}
public void inorder() {
inorder(root);
}
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(40);
tree.insert(50);
tree.insert(60);
tree.insert(70);
System.out.println("红黑树中序遍历:");
tree.inorder();
}
}
这个实现中,左旋和右旋操作用于保持红黑树的性质。在插入操作中,先将新插入的节点标记为红色,然后通过 fixInsert()
方法来修复可能违反红黑树性质的地方。
让我们逐个讲解代码中的每个方法:
-
RedBlackTreeNode
类:- 定义了红黑树节点的结构。
- 每个节点包含
data
(节点的值)、parent
(父节点的引用)、left
(左子节点的引用)、right
(右子节点的引用)、color
(节点的颜色,0 表示黑色,1 表示红色)。
-
RedBlackTree
类:root
:红黑树的根节点。
-
leftRotate(RedBlackTreeNode x)
方法:- 实现了左旋操作。
- 参数
x
是要进行左旋的节点。 - 将
x
的右子节点y
上移为x
的父节点,x
成为y
的左子节点。 - 旋转后要更新节点的父节点引用。
-
rightRotate(RedBlackTreeNode y)
方法:- 实现了右旋操作。
- 参数
y
是要进行右旋的节点。 - 将
y
的左子节点x
上移为y
的父节点,y
成为x
的右子节点。 - 旋转后要更新节点的父节点引用。
-
insert(int data)
方法:- 插入新节点到红黑树中。
- 首先创建一个新节点,并按照二叉查找树的规则找到其应该插入的位置。
- 将新节点插入,并将其颜色标记为红色。
- 调用
fixInsert()
方法修正红黑树的性质。
-
fixInsert(RedBlackTreeNode k)
方法:- 修复插入节点后可能违反红黑树性质的问题。
- 参数
k
是刚刚插入的节点。 - 通过对
k
的父节点、叔节点和祖父节点的颜色进行判断和旋转操作来保持红黑树的性质。
-
inorder(RedBlackTreeNode root)
方法:- 中序遍历红黑树。
- 递归地从左子树开始遍历,然后访问当前节点,最后递归地遍历右子树。
-
inorder()
方法:- 对外提供的中序遍历方法的入口点。
-
main(String[] args)
方法:- 测试主方法,创建一个红黑树对象,并插入一些节点,然后进行中序遍历以验证红黑树的正确性。
这些方法共同构成了一个简单的红黑树的实现,通过这些方法可以实现对红黑树的插入操作,并保持其性质。
红黑树通过保持以下两个性质来维持平衡:
-
黑色平衡性(Black-Height Balance):确保从根到叶子的每条路径上的黑色节点数目相同。换句话说,红黑树保证了所有路径上的黑色节点数目相等,这样就能够保证树的高度不会过高。
-
红色性质(Red Property):保证没有两个相邻的红色节点,也就是说,红色节点不能出现相连的情况。
这两个性质的作用如下:
- 黑色平衡性 确保了红黑树的高度不会过高,使得其插入、删除等操作的时间复杂度保持在 O(log n) 级别。
- 红色性质 则是为了防止出现连续的红色节点,这样可以避免了路径上出现两个相邻的红色节点,保证了在插入、删除等操作后的平衡性。
综合这两个性质,红黑树能够保持平衡,并且在动态插入、删除节点时,通过旋转和重新着色等操作来维护这些性质,从而保持树的平衡性。