一、定义
红黑树是一种特殊的二叉查找树,它的每一个结点都被标记为红色或者黑色。是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体。
二、性质
将一棵标准二叉树的所有空指针用外部结点替代,就生成了一棵扩展二叉树。红黑树以扩展二叉树为基础,又加入了以下性质:
- 根结点和所有外部结点都是黑色;
- 所有从根结点至外部结点的路径上,不能包含两个连续的红色结点;
- 所有从根结点至外部结点的路径上,包含相同数目的黑色结点。
我们可以把所有指向黑色结点标记为黑色,把所有指向红色结点的指针标记为红色,从而获得红黑树的一种等价的定义:
- 从内部结点指向外部结点的指针都为黑色;
- 所有从根结点到外部结点的路径上,不能包含两个连续的红色指针;
- 所有从根结点到外部结点的路径上,包含相同数目的黑色指针。
我们可以根据指针的颜色推断出结点的颜色,反之亦然。这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
三、操作
因为每一个红黑树也是一个特化的二叉搜索树,因此红黑树上的只读操作与普通二叉搜索树上的只读操作相同。然而,在红黑树上进行插入操作和删除操作会导致不再符合红黑树的性质。恢复红黑树的属性需要少量(O(log n))的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为 O(log n) 。
1、插入
我们首先以二叉搜索树的方法增加节点并标记它为红色。(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。) 下面要进行什么操作取决于其他临近节点的颜色。同人类的家族树中一样,我们将使用术语叔父节点来指一个节点的父节点的兄弟节点。注意:
性质1 节点是红色或黑色。 所有叶子都是黑色。总是保持着。
性质2 每个红色节点的两个子节点都是黑色。只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
性质3 从每个叶子到根的所有路径都包含相同数目的黑色节点。只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。
在下面的示意图中,将要插入的节点标为N,N的父节点标为P,N的祖父节点标为G,N的叔父节点标为U。在图中展示的任何颜色要么是由它所处情形所作的假定,要么是这些假定所暗含的。
情形1: 新节点N位于树的根上,没有父节点。在这种情形下,我们把它重绘为黑色以满足性质1 根是黑色。因为它在每个路径上对黑节点数目增加一,性质3符合。
情形2: 新节点的父节点P是黑色,所以性质2没有失效(新节点是红色的)。在这种情形下,树仍是有效的。性质3受到威胁,因为新节点N有两个黑色叶子儿子;但是由于新节点N是红色,通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径同样数目的黑色节点,所以这个性质依然满足。
情形3: 如果父节点P和叔父节点U二者都是红色,则我们可以将它们两个重绘为黑色并重绘祖父节点G为红色(用来保持性质3)。现在我们的新节点N有了一个黑色的父节点P。因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变。但是,红色的祖父节点G的父节点也有可能是红色的,这就违反了性质2。为了解决这个问题,我们在祖父节点G上递归地进行情形1的整个过程。
情形4: 父节点P是红色而叔父节点U是黑色或缺少; 还有,新节点N是其父节点P的右子节点,而父节点P又是其父节点的左子节点。在这种情形下,我们进行一次左旋转调换新节点和其父节点的角色; 接着,我们按情形5处理以前的父节点P。这导致某些路径通过它们以前不通过的新节点N或父节点P中的一个,但是这两个节点都是红色的,所以性质3没有失效。
2、删除
在本文余下的部分中,我们只需要讨论删除只有一个儿子的节点(如果它两个儿子都为空,即均为叶子,我们任意将其中一个看作它的儿子)。如果我们删除一个红色节点,它的父亲和儿子一定是黑色的。所以我们可以简单的用它的黑色儿子替换它,并不会破坏属性2和3。通过被删除节点的所有路径只是少了一个红色节点,这样可以继续保证属性3。另一种简单情况是在被删除节点是黑色而它的儿子是红色的时候。如果只是去除这个黑色节点,用它的红色儿子顶替上来的话,会破坏属性2,但是如果我们重绘它的儿子为黑色,则曾经通过它的所有路径将通过它的黑色儿子,这样可以继续保持属性2。
需要进一步讨论的是在要删除的节点和它的儿子二者都是黑色的时候,这是一种复杂的情况。我们首先把要删除的节点替换为它的儿子。出于方便,称呼这个儿子为N,称呼它的兄弟(它父亲的另一个儿子)为S。在下面的示意图中,我们还是使用P称呼N的父亲,SL称呼S的左儿子,SR称呼S的右儿子。
情况 1: N 是新的根。在这种情况下,我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所以属性都保持着。
情况 2: S 是红色。在这种情况下我们在N的父亲上做左旋转,把红色兄弟转换成N的祖父。我们接着对调 N 的父亲和祖父的颜色。尽管所有的路径仍然有相同数目的黑色节点,现在 N 有了一个黑色的兄弟和一个红色的父亲,所以我们可以接下去按 4、5或6情况来处理。(它的新兄弟是黑色因为它是红色S的一个儿子。)
情况 5: S 是黑色,S 的左儿子是红色,S 的右儿子是黑色,而 N 是它父亲的左儿子。在这种情况下我们在 S 上做右旋转,这样 S 的左儿子成为 S 的父亲和 N 的新兄弟。我们接着交换 S 和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在 N 有了一个右儿子是红色的黑色兄弟,所以我们进入了情况 6。N 和它的父亲都不受这个变换的影响。
- 它通过 N 的新兄弟。那么它以前和现在都必定通过 S 和 N 的父亲,而它们只是交换了颜色。所以路径保持了同样数目的黑色节点。
- 它通过 N 的新叔父,S 的右儿子。那么它以前通过 S、S 的父亲和 S 的右儿子,但是现在只通过 S,它被假定为它以前的父亲的颜色,和 S 的右儿子,它被从红色改变为黑色。合成效果是这个路径通过了同样数目的黑色节点。
四、源码实现
struct Key {
int value;
};
struct RBTNode {
Key key;
int lcount;
int rcount;
RBTNode* lchild;
RBTNode* rchild;
RBTNode* parent;
bool color;
};
class RBT {
private:
const static bool RED = true;
const static bool BLACK = false;
RBTNode* m_null;
RBTNode* m_root;
void clear() {//
RBTNode* p = m_root;
while (p != m_null) {
if (p->lchild != m_null) {
p = p->lchild;
}else if (p->rchild != m_null) {
p = p->rchild;
}else {
RBTNode* temp = p;
p = p->parent;
if (temp == p->lchild) {
p->lchild = m_null;
}else {
p->rchild = m_null;
}
delete temp;
}
}
m_root = m_null;
}
void delFixup(RBTNode* delNode) {
RBTNode* p = delNode;
while (p != m_root && p->color == BLACK) {
if (p == p->parent->lchild) {
RBTNode* sibling = p->parent->rchild;
if (sibling->color == RED) {
sibling->color = BLACK;
p->parent->color = RED;
leftRotate(p->parent);
sibling = p->parent->rchild;
}
if (sibling->lchild->color == BLACK
&& sibling->rchild->color == BLACK
) {
sibling->color = RED;
p = p->parent;
}else {
if (sibling->rchild->color == BLACK) {
sibling->lchild->color = BLACK;
sibling->color = RED;
rightRotate(sibling);
sibling = sibling->parent;
}
sibling->color = sibling->parent->color;
sibling->parent->color = BLACK;
sibling->rchild->color = BLACK;
leftRotate(sibling->parent);
p = m_root;
}
}else {
RBTNode* sibling = p->parent->lchild;
if (sibling->color == RED) {
sibling->color = BLACK;
p->parent->color = RED;
rightRotate(p->parent);
sibling = p->parent->lchild;
}
if (sibling->lchild->color == BLACK
&& sibling->rchild->color == BLACK
) {
sibling->color = RED;
p = p->parent;
}else {
if (sibling->lchild->color == BLACK) {
sibling->rchild->color = BLACK;
sibling->color = RED;
leftRotate(sibling);
sibling = sibling->parent;
}
sibling->color = sibling->parent->color;
sibling->parent->color = BLACK;
sibling->lchild->color = BLACK;
rightRotate(sibling->parent);
p = m_root;
}
}
}
p->color = BLACK;
}
void insertFixup(RBTNode* insertNode) {
RBTNode* p = insertNode;
while (p->parent->color == RED) {
if (p->parent == p->parent->parent->lchild) {
RBTNode* parentRight = p->parent->parent->rchild;
if (parentRight->color == RED) {
p->parent->color = BLACK;
parentRight->color = BLACK;
p->parent->parent->color = RED;
p = p->parent->parent;
}else {
if (p == p->parent->rchild) {
p = p->parent;
leftRotate(p);
}
p->parent->color = BLACK;
p->parent->parent->color = RED;
rightRotate(p->parent->parent);
}
}else {
RBTNode* parentLeft = p->parent->parent->lchild;
if (parentLeft->color == RED) {
p->parent->color = BLACK;
parentLeft->color = BLACK;
p->parent->parent->color = RED;
p = p->parent->parent;
}else {
if (p == p->parent->lchild) {
p = p->parent;
rightRotate(p);
}
p->parent->color = BLACK;
p->parent->parent->color = RED;
leftRotate(p->parent->parent);
}
}
}
m_root->color = BLACK;
}
inline int keyCmp(const Key& key1, const Key& key2) {
//比较两个Key的大小。这里可能有更复杂的比较,如字符串比较等。
return key1.value - key2.value;
}
inline void leftRotate(RBTNode* node) {
//把一个节点向左下方移一格,并让他原来的右子节点代替它的位置。
RBTNode* right = node->rchild;
node->rchild = right->lchild;
node->rcount = right->lcount;
node->rchild->parent = node;
right->parent = node->parent;
if (right->parent == m_null) {
m_root = right;
}else if (node == node->parent->lchild) {
node->parent->lchild = right;
}else {
node->parent->rchild = right;
}
right->lchild = node;
right->lcount += node->lcount + 1;
node->parent = right;
}
inline void rightRotate(RBTNode* node) {
//把一个节点向右下方移一格,并让他原来的左子节点代替它的位置。
RBTNode* left = node->lchild;
node->lchild = left->rchild;
node->lcount = left->rcount;
node->lchild->parent = node;
left->parent = node->parent;
if (left->parent == m_null) {
m_root = left;
}else if (node == node->parent->lchild) {
node->parent->lchild = left;
}else {
node->parent->rchild = left;
}
left->rchild = node;
left->rcount += node->rcount + 1;
node->parent = left;
}
RBTNode* treeMax(RBTNode* root) {//找到子树中最大的一个节点
RBTNode* result = root;
while (result->rchild != m_null) {
result = result->rchild;
}
return result;
}
RBTNode* treeMin(RBTNode* root) {//找到子树中最小的一个节点
RBTNode* result = root;
while (result->lchild != m_null) {
result = result->lchild;
}
return result;
}
public:
RBT() {
m_null = new RBTNode;
m_null->color = BLACK;
m_null->lchild = m_null->rchild = m_null;
m_root = m_null;
}
~RBT() {
clear();
delete m_null;
}
RBTNode* atIndex(int i) {//找到从小到大排序后下标为i的节点。i从0开始。
RBTNode* result = m_root;
if (i > result->lcount + result->rcount) {
result = NULL;
}else {
while (i != result->lcount) {
if (i < result->lcount) {
result = result->lchild;
}else {
i -= result->lcount + 1;
result = result->rchild;
}
}
}
return result;
}
void del(RBTNode* node) {//删除一个节点
RBTNode* toDel = node;
if (node->lchild != m_null && node->rchild != m_null) {
toDel = treeNext(node);//找到中序后继:即右子树最左节点
}
RBTNode* temp = toDel;
while (temp->parent != m_null) {
if (temp == temp->parent->lchild) {
temp->parent->lcount--;
}else {
temp->parent->rcount--;
}
temp = temp->parent;
}
RBTNode* replace = toDel->lchild != m_null? toDel->lchild: toDel->rchild;
replace->parent = toDel->parent;
if (replace->parent == m_null) {
m_root = replace;
}else if (toDel == toDel->parent->lchild) {
replace->parent->lchild = replace;
}else {
replace->parent->rchild = replace;
}
if (toDel != node) {
node->key = toDel->key;
}
if (toDel->color == BLACK) {
//修改树,以保持平衡。
delFixup(replace);
}
delete toDel;
}
void insert(const Key& key) {//插入一个节点
RBTNode* node = new RBTNode;
node->key = key;
node->lcount = 0;
node->rcount = 0;
node->lchild = m_null;
node->rchild = m_null;
node->color = RED;
RBTNode* p = m_root;
RBTNode* leaf = m_null;
while (p != m_null) {
leaf = p;
if (keyCmp(node->key, p->key) < 0) {
p->lcount++;
p = p->lchild;
}else {
p->rcount++;
p = p->rchild;
}
}
node->parent = leaf;
if (leaf == m_null) {//如果是空树。
m_root = node;
}else if (keyCmp(node->key, leaf->key) < 0) {
leaf->lchild = node;
}else {
leaf->rchild = node;
}
//修改树,以保持平衡。
insertFixup(node);
}
int nodeCount() {
return m_root != m_null? m_root->lcount + m_root->rcount + 1: 0;
}
RBTNode* search(const Key& key) {//按照key查找一个节点。
RBTNode* result = m_root;
while (result != m_null && keyCmp(key, result->key) != 0) {
result = keyCmp(key, result->key) < 0 ? result->lchild : result->rchild;
}
return result == m_null? NULL: result;
}
void toArray(int* array) {//把树中节点的值放进一个数组。
RBTNode* p = treeMin(m_root);
int i = 0;
while (p != m_null) {
array[i] = p->key.value;
i++;
p = treeNext(p);
}
}
RBTNode* treeNext(RBTNode* node) {//一个节点在中序遍列中的下一个节点。后继
RBTNode* result;
if (node->rchild != m_null) {
result = treeMin(node->rchild);
}else {
result = node->parent;
RBTNode* temp = node;
while (result != m_null && temp == result->rchild) {
temp = result;
result = result->parent;
}
}
return result;
}
RBTNode* treePre(RBTNode* node) {//一个节点在中序遍列中的前一个节点。前驱
RBTNode* result;
if (node->lchild != m_null) {
result = treeMax(node->lchild);
}else {
result = node->parent;
RBTNode* temp = node;
while (result != m_null && temp == result->lchild) {
temp = result;
result = result->parent;
}
}
return result;
}
};