详细内容,参见July博文:教你初步了解红黑树
基本概念
红黑树本质上是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
红黑树的5个性质:
- 每个结点要么是红的要么是黑的。
- 根结点是黑的。
- 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
- 如果一个结点是红的,那么它的两个儿子都是黑的。
- 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
树的旋转
当在对红黑树进行插入和删除等操作时,对树做了修改可能会破坏红黑树的性质。为了继续保持红黑树的性质,可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即通过修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后继续保持它的性质或平衡的目的。
树的旋转分为左旋和右旋
1. 左旋
关于旋转,有篇博文【作者:寒江独钓】写的很不错,做了个动画,一看即懂:浅谈算法和数据结构(9):平衡查找树之红黑树
当在某个结点pivot上做左旋操作时,设它的右孩子y不是NIL[T],pivot可以为任何不是NIL[T]的左子结点。
左旋以pivot到Y之间的链为“支轴”进行,它使Y成为该子树的新根,而Y的左孩子b则成为pivot的右孩子。
private TreeNode<Integer> rotateLeft(TreeNode<Integer> currentNode){
TreeNode<Integer> rightChild=currentNode.getRightChild();
//将rightChild的左节点复制给currentNode右节点
currentNode.setRightChild(rightChild.getLeftChild());
//将currentNode复制给rightChild的右节点
rightChild.setLeftChild(currentNode);
rightChild.setColor(currentNode.getColor());
currentNode.setColor("RED");
return rightChild;
}
2. 右旋
左旋的逆操作
private TreeNode<Integer> rotateRight(TreeNode<Integer> currentNode){
TreeNode<Integer> leftChild=currentNode.getLeftChild();
//将rightChild的左节点复制给currentNode右节点
currentNode.setLeftChild(leftChild.getRightChild());
//将currentNode复制给rightChild的右节点
leftChild.setRightChild(currentNode);
leftChild.setColor(currentNode.getColor());
currentNode.setColor("RED");
return leftChild;
}
基本操作
1. 插入
参考BST的插入,红黑树的插入操作包括:插入+插入修复(恢复平衡)
待插入的结点为z,红黑树的插入伪代码如下所示:
RB-INSERT(T, z)
1 y ← nil[T]
2 x ← root[T]
3 while x ≠ nil[T]
4 do y ← x
5 if key[z] < key[x]
6 then x ← left[x]
7 else x ← right[x]
8 p[z] ← y
9 if y = nil[T]
10 then root[T] ← z
11 else if key[z] < key[y]
12 then left[y] ← z
13 else right[y] ← z
14 left[z] ← nil[T]
15 right[z] ← nil[T]
16 color[z] ← RED
17 RB-INSERT-FIXUP(T, z)
- 如果插入的是根结点,因为原树是空树,此情况只会违反性质2,所以直接把此结点涂为黑色。
- 如果插入的结点的父结点是黑色,由于此不会违反性质2和性质4,红黑树没有被破坏,所以此时也是什么也不做。
但当遇到下述3种情况时:
- 插入修复情况1:如果当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色
- 插入修复情况2:当前结点的父结点是红色,叔叔结点是黑色,当前结点是其父结点的右子
- 插入修复情况3:当前结点的父结点是红色,叔叔结点是黑色,当前结点是其父结点的左子
我们需要进行插入修复:
RB-INSERT-FIXUP(T,z)
1 while color[p[z]] = RED
2 do if p[z] = left[p[p[z]]]
3 then y ← right[p[p[z]]]
4 if color[y] = RED
5 then color[p[z]] ← BLACK ▹ Case 1
6 color[y] ← BLACK ▹ Case 1
7 color[p[p[z]]] ← RED ▹ Case 1
8 z ← p[p[z]] ▹ Case 1
9 else if z = right[p[z]]
10 then z ← p[z] ▹ Case 2
11 LEFT-ROTATE(T, z) ▹ Case 2
12 color[p[z]] ← BLACK ▹ Case 3
13 color[p[p[z]]] ← RED ▹ Case 3
14 RIGHT-ROTATE(T, p[p[z]]) ▹ Case 3
15 else (same as then clause
with "right" and "left" exchanged)
16 color[root[T]] ← BLACK
2. 删除
。。。。。