红黑树(英语:Red–black tree)是一种自平衡二叉查找树,典型用途是实现关联数组。
红黑树的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在O(log n)时间内完成查找、插入和删除,这里的n是树中元素的数目。
红黑树的性质
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
- 节点是红色或黑色。
- 根是黑色。
- 所有叶子都是黑色(叶子是NIL节点)。
- 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
如果路径上的各顶点均不互相重复,称这样的路径为简单路径。
这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。
要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
维基百科上说红黑树最短可能路径全是黑色节点,不知道是否正确,但至少最短可能路径的红色节点是最少的。根据性质5,最长可能路径的红色节点最多,但是根据性质4,一条路径的红色节点不可能比黑色多。所以假设最短可能路径是n个黑色节点,则最长可能路径不超过2n个节点。
小技巧
插入或删除之前红黑树是满足性质的。
红黑树有父节点,故可通过一级指针(旋转的时候)修改节点的指向,如果旋转节点指针x不指向root节点,则修改parrent和left,right指针;如果指向root节点,则直接修改root指针。当然是用二级指针或一级指针的引用也可解决。
插入节点
以下,N是插入节点,P是父节点,U是叔父节点,G是祖父节点。
插入节点代码:
// n是插入节点
void rbtree_insert(RBTree* t, RBTreeNode* n) {
RBTreeNode* x = t->root;
RBTreeNode* y = x;
while (x != t->nil) {
y = x;
if (x->key > n->key) {
x = x->lchild;
}
else if (x->key < n->key) {
x = x->rchild;
}
else {
return;
}
}
if (y != t->nil) {
if (y->key > n->key) {
y->lchild = n;
}
else {
y->rchild = n;
}
}
else {
t->root = n;
}
n->parent = y;
rbtree_insert_fixup(t, n);
}
插入节点后需要修复红黑树性质。
由于插入黑色节点破坏了性质5且很难调整,故插入的节点默认设为红色。
- 性质1和性质3总是保持着。
- 性质4只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
- 性质5只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。
插入主要有5种情况:
- 父节点为空
- 父节点为黑色
- 父节点是红色,并且叔节点也是红色
- 父节点P是红色,并且叔节点是黑色(可能是叶子节点),父节点是左孩子,插入节点N是左孩子(LL型)。
- 父节点P是红色,并且叔节点是黑色(可能是叶子节点),父节点是左孩子,插入节点N是右孩子(LR型)。
情况1
父节点为空。插入节点就是根节点,设为黑色。
// case 1: x是根节点,变黑
if (x->parent == t->nil) {
x->color = BLACK;
return;
}
情况2
父节点为黑色,没有破坏性质。
// case 2: x的父亲是黑色,x保持红色
if (BLACK == x->parent->color) {
x->color = RED;
return;
}
情况3
父节点是红色,并且叔节点也是红色。肯定有祖父节点,父节点和叔节点变为黑色,祖父节点变为红色,将祖父节点设为插入节点继续从情况1判断。
// 父节点是红色,有祖父节点(且是黑色)
RBTreeNode* u = uncle(x);
/* case 3
* 父节点和叔节点都是红色,父辈变黑,祖父节点变红,x保持红色
* x是红色,父辈和祖父交换颜色,黑高没有变,没有破坏性质5
* 祖父节点是红色,祖父的父亲可能是红色或nil,为了不破坏性质2和性质4,
* 将插入节点变为祖父节点,继续走流程
*/
if (RED == u->color) {
x->parent->color = BLACK;
u->color = BLACK;
x->parent->parent->color = RED;
x->color = RED;
x = x->parent->parent;
rbtree_insert_fixup(t, x);
}
情况4
父节点P是红色,并且叔节点是黑色(可能是叶子节点),父节点是左孩子,插入节点N是左孩子(LL型)。祖父节点肯定是黑色。插入节点保持红色,将父节点变为黑色,祖父节点变为红色,然后对祖父节点G右旋。
情况5
父节点P是红色,并且叔节点是黑色(可能是叶子节点),父节点是左孩子,插入节点N是右孩子(LR型)。祖父节点肯定是黑色。祖父节点变为红色,插入节点变为黑色,先对原父节点左旋,再对原祖父节点右旋。(代码实现上可以先将LR型先转为LL型,然后按情况4处理)
详图:
// 父节点是红色,叔节点是黑色
else {
if (x->parent == x->parent->parent->lchild) {
/* case 4
* LL型:对祖父节点右旋
* 将父节点变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过父节点,黑高不变
*/
if (x == x->parent->lchild) {
x->parent->color = BLACK;
x->parent->parent->color = RED;
x->color = RED;
rbtree_rotate_right(t, x->parent->parent);
}
/* case 5
* LR型:先对父节点左旋再对祖父节点右旋
* 将插入节点x变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过插入节点x,黑高不变
*/
else {
x->parent->parent->color = RED;
x->color = BLACK;
rbtree_rotate_left(t, x->parent);
// 经过旋转x的祖父变成x的父亲
rbtree_rotate_right(t, x->parent);
}
}
// 记住上面LL,LR型,下面的RR,RL型,只需要将左右孩子互换,左右旋互换即可
else {
/* case 4
* RR型:对祖父节点左旋
* 将父节点变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过父节点,黑高不变
*/
if (x == x->parent->rchild) {
x->parent->color = BLACK;
x->parent->parent->color = RED;
x->color = RED;
rbtree_rotate_left(t, x->parent->parent);
}
/* case 5
* RL型:先对父节点右旋再对祖父节点左旋
* 将插入节点x变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过插入节点x,黑高不变
*/
else {
x->parent->parent->color = RED;
x->color = BLACK;
rbtree_rotate_right(t, x->parent);
// 经过旋转x的祖父变成x的父亲
rbtree_rotate_left(t, x->parent);
}
}
}
注意:情况4和5有对称类型(RL型和RR型),此时将描述的左右互换即可。情况3,4,5共同点是父节点是红色,不同点是叔节点颜色和旋转类型。
情况4,5对应的四种类型分别是:
- 父节点是左孩子,插入节点是左孩子 (LL型)
- 父节点是左孩子,插入节点是右孩子 (LR型)
- 父节点是右孩子,插入节点是右孩子 (RR型)
- 父节点是右孩子,插入节点是左孩子 (RL型)
删除节点
思路:如果需要删除的节点有两个儿子,那么问题可以被转化成删除另一个只有一个儿子的节点的问题。(为了表述方便,这里所指的儿子,为非叶子节点的儿子)接下来只考虑删除最多1个儿子的节点(可以为叶子节点),以下代码中,实际删除节点r为想要删除节点d的后继节点(当然也可以选前驱)。
删除操作中有三个需要注意的节点:
- 想要删除节点d
- 实际删除的节点r(后继节点)
- 实际删除节点的儿子cur(当前节点,修复代码中操作的节点)
实际删除的节点r如果是红色,那么儿子都是叶子,直接删除,没有破坏性质。
实际删除的节点r是黑色并且儿子cur是红色,r替换为cur,cur设为黑色,修复完成。
实际删除的节点r是黑色并且儿子cur是黑色,这时候两个儿子都是叶子节点,r替换为cur,经过cur的路径黑高减1,性质5被破坏。需要修复性质。
删除节点代码:
/*
* d是想要删除节点
* r是实际删除的节点
* cur是实际删除节点的儿子(当前节点)
* 找到d的后继r
*/
RBTreeNode* rbtree_delete(RBTree* t, RBTreeNode* d) {
RBTreeNode* r;
RBTreeNode* cur;
// 有两个非叶子节点则查找后继节点
if (d->lchild != t->nil && d->rchild != t->nil) {
r = rbtree_suffix(t, d);
}
else {
r = d;
}
if (r->lchild != t->nil) {
cur = r->lchild;
}
else {
cur = r->rchild;
}
// 实际删除的节点r替换为它的儿子n
if (r->parent == t->nil) {
t->root = cur;
}
else if (r == r->parent->lchild) {
r->parent->lchild = cur;
}
else {
r->parent->rchild = cur;
}
cur->parent = r->parent;
// 想要删除的节点不用实际删除,替换key,value就行
if (r != d) {
d->key = r->key;
d->value = r->value;
}
// 修复性质
if (BLACK == r->color) {
rbtree_delete_fixup(t, cur);
}
return r;
}
以下N是实际删除节点的儿子(当前节点),P是父节点,S是N的兄弟节点,SL是S的左儿子,SR是S的右儿子。
删除主要有5种情况:
以下假设当前节点是左儿子,如果是右儿子则5种情况的左右互换。
- 当前节点N是根节点,没有破坏性质
- 兄弟节点S是红色
- 兄弟节点S是黑色,S的两个儿子也是黑色
- 兄弟节点S是黑色,S的左儿子SL是红色,S的右儿子SR是黑色
- 兄弟节点S是黑色,S的右儿子SR是红色
情况1
当前节点N是根节点,没有破坏性质。
/*
* 实际删除节点是黑色,儿子节点是红色时会走到这
* 情况1也会走到这
* 如果当前节点是根节点或者是红色,则将颜色改为黑色
*/
cur->color = BLACK;
情况2
兄弟节点S是红色。
父节点P改为红色,兄弟节点S改为黑色,对P左旋,兄弟节点变为旋转前的SL。由于未修复性质5,继续按后续情况处理。
/*
* cur是实际删除节点的儿子
* 旋转要更新兄弟节点
*/
void rbtree_delete_fixup(RBTree* t, RBTreeNode* cur) {
while (cur != t->root && BLACK == cur->color) {
RBTreeNode* s = rbtree_sibling(cur);
if (cur == cur->parent->lchild) {
/*
* 情况2 兄弟节点是红色
* 父节点P改为红色,兄弟节点S改为黑色,对P左旋,兄弟节点变为旋转前的SL
* 变色旋转后的各路径黑高没有改变
* 但由于通过N节点的黑高仍少1,继续按后续情况处理
* 所以兄弟节点如果是红色则转为黑色处理
*/
if (RED == s->color) {
cur->parent->color = RED;
s->color = BLACK;
rbtree_rotate_left(t, cur->parent);
s = rbtree_sibling(cur);
}
情况3
兄弟节点S是黑色,S的两个儿子也是黑色。
将S变为红色,通过S的路径黑高减1,此时通过N和通过S的路径的黑高相同,但是通过P的路径的黑高还是比不通过P的路径的黑高少1,所以还需要继续修复。将当前节点改为父节点P。
注意这里P可能是黑色也可能是红色,这两种情况统一处理了,如果P是红色,则退出循环,将当前节点变为黑色,避免破坏性质4(P,S都是红色)。
// 以下兄弟节点S是黑色
/*
* 情况3 兄弟节点S是黑色,S的两个儿子也是黑色
* 将S变为红色,通过S的路径黑高减1,此时通过N和通过S的路径的黑高相同,
* 但是通过P的路径的黑高还是比不通过P的路径的黑高少1,所以还需要继续修复。
* 将当前节点改为父节点P。
* 注意这里P可能是黑色也可能是红色,这两种情况统一处理了,
* 如果P是红色,则退出循环,将当前节点变为黑色,避免破坏性质4(P,S都是红色)
*/
if (BLACK == s->lchild->color && BLACK == s->rchild->color) {
s->color = RED;
cur = cur->parent;
}
情况4
兄弟节点S是黑色,S的左儿子SL是红色,S的右儿子SR是黑色。
交换兄弟节点S和左儿子SL的颜色,对S右旋。旋转和变色后各路径的黑高和原来相同,通过N的路径还是黑高少1,通过SR的路径黑高不变,此时满足情况5,跳到情况5处理。
情况5
兄弟节点S是黑色,S的右儿子SR是红色
父节点P和兄弟节点S互换颜色,右儿子SR变为黑色,然后对P左旋。
修复后,原来通过N节点的路径多了个黑色节点P,黑高加1;原来通过SL的路径黑高不变(P->S->SL变为S->P->SL,而SL在旋转后变为新兄弟);原来通过SR的路径黑高也不变(P->S->SR变为S->SR,少了个红节点不影响,而SR在旋转后变为N的叔父)。此时性质5已修复。
else {
/*
* 情况4 兄弟节点S是黑色,S的左儿子SL是红色,S的右儿子SR是黑色
* 交换兄弟节点S和左儿子SL的颜色,对S右旋。
* 旋转和变色后各路径的黑高和原来相同,
* 通过N的路径还是黑高少1,通过SR的路径黑高不变,此时满足情况5,跳到情况5处理。
*/
if (RED == s->lchild->color && BLACK == s->rchild->color) {
s->color = RED;
s->lchild->color = BLACK;
rbtree_rotate_right(t, s);
s = rbtree_sibling(cur);
}
/*
* 情况5 兄弟节点S是黑色,S的右儿子SR是红色
* 父节点P和兄弟节点S互换颜色,右儿子SR变为黑色,然后对P左旋
* 修复后,原来通过N节点的路径多了个黑色节点P,黑高加1;
* 原来通过SL的路径黑高不变(P->S->SL变为S->P->SL,而SL在旋转后变为新兄弟);
* 原来通过SR的路径黑高也不变(P->S->SR变为S->SR,
* 少了个红节点不影响,而SR在旋转后变为N的叔父)
* 此时性质5已修复。
*/
if (RED == s->rchild->color) {
s->color = cur->parent->color;
cur->parent->color = BLACK;
s->rchild->color = BLACK;
rbtree_rotate_left(t, cur->parent);
// 红黑树已修复,退出循环
cur = t->root;
}
}
红黑树完整代码:
/*
* 五个性质:
* 1. 节点非红即黑
* 2. 根节点是黑色
* 3. 叶子节点都是黑色(nil)
* 4. 红色节点的两个孩子都是黑色(没有连续的红节点)
* 5. 任一节点,经过它到叶子节点的所有简单路径的黑色节点树相同(黑高相同)
* 比较的是key
*/
#include <stdio.h>
#include <stdlib.h>
#define RED 1
#define BLACK 2
typedef int KeyType;
// compare函数
typedef struct _RBTreeNode {
int color;
struct _RBTreeNode* lchild;
struct _RBTreeNode* rchild;
struct _RBTreeNode* parent;
KeyType key;
void* value;
} RBTreeNode;
typedef struct _RBTree {
RBTreeNode* root;
RBTreeNode* nil; // 叶子节点
} RBTree;
// 父节点是x
void rbtree_rotate_left(RBTree* t, RBTreeNode* x) {
RBTreeNode* y = x->rchild;
x->rchild = y->lchild;
// 这一步忘了
if (y->lchild != t->nil) {
y->lchild->parent = x;
}
y->lchild = x;
// 这一步没想出来。由于传的是一级指针x,不能通过指针x直接改变root节点,所以如果x指向根节点则修改root指向为新的根节点
if (x->parent == t->nil) {
t->root = y;
}
else if (x == x->parent->lchild) {
x->parent->lchild = y;
}
else {
x->parent->rchild = y;
}
y->parent = x->parent;
x->parent = y;
}
// 父节点是y
// 将rbtree_rotate_left中的x和y互换,将lchild和rchild互换
void rbtree_rotate_right(RBTree* t, RBTreeNode* y) {
RBTreeNode* x = y->lchild;
y->lchild = x->rchild;
if (x->rchild != t->nil) {
x->rchild->parent = y;
}
x->rchild = y;
if (y->parent == t->nil) {
t->root = x;
}
else if (y == y->parent->lchild) {
y->parent->lchild = x;
}
else {
y->parent->rchild = x;
}
x->parent = y->parent;
y->parent = x;
}
RBTreeNode* uncle(RBTreeNode* x) {
if (x->parent == x->parent->parent->lchild) {
return x->parent->parent->rchild;
}
else {
return x->parent->parent->lchild;
}
}
// x是插入节点
void rbtree_insert_fixup(RBTree* t, RBTreeNode* x) {
// case 1: x是根节点,变黑
if (x->parent == t->nil) {
x->color = BLACK;
return;
}
// case 2: x的父亲是黑色,x保持红色
if (BLACK == x->parent->color) {
x->color = RED;
return;
}
// 父节点是红色,有祖父节点(且是黑色)
RBTreeNode* u = uncle(x);
/* case 3
* 父节点和叔节点都是红色,父辈变黑,祖父节点变红,x保持红色
* x是红色,父辈和祖父交换颜色,黑高没有变,没有破坏性质5
* 祖父节点是红色,祖父的父亲可能是红色或nil,为了不破坏性质2和性质4,
* 将插入节点变为祖父节点,继续走流程
*/
if (RED == u->color) {
x->parent->color = BLACK;
u->color = BLACK;
x->parent->parent->color = RED;
x->color = RED;
x = x->parent->parent;
rbtree_insert_fixup(t, x);
}
// 父节点是红色,叔节点是黑色
else {
if (x->parent == x->parent->parent->lchild) {
/* case 4
* LL型:对祖父节点右旋
* 将父节点变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过父节点,黑高不变
*/
if (x == x->parent->lchild) {
x->parent->color = BLACK;
x->parent->parent->color = RED;
x->color = RED;
rbtree_rotate_right(t, x->parent->parent);
}
/* case 5
* LR型:先对父节点左旋再对祖父节点右旋
* 将插入节点x变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过插入节点x,黑高不变
*/
else {
x->parent->parent->color = RED;
x->color = BLACK;
rbtree_rotate_left(t, x->parent);
// 经过旋转x的祖父变成x的父亲
rbtree_rotate_right(t, x->parent);
}
}
// 记住上面LL,LR型,下面的RR,RL型,只需要将左右孩子互换,左右旋互换即可
else {
/* case 4
* RR型:对祖父节点左旋
* 将父节点变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过父节点,黑高不变
*/
if (x == x->parent->rchild) {
x->parent->color = BLACK;
x->parent->parent->color = RED;
x->color = RED;
rbtree_rotate_left(t, x->parent->parent);
}
/* case 5
* RL型:先对父节点右旋再对祖父节点左旋
* 将插入节点x变为黑色,祖父节点变为红色
* 旋转前通过原祖父节点的路径在旋转后都通过插入节点x,黑高不变
*/
else {
x->parent->parent->color = RED;
x->color = BLACK;
rbtree_rotate_right(t, x->parent);
// 经过旋转x的祖父变成x的父亲
rbtree_rotate_left(t, x->parent);
}
}
}
}
// n是插入节点
void rbtree_insert(RBTree* t, RBTreeNode* n) {
RBTreeNode* x = t->root;
RBTreeNode* y = x;
while (x != t->nil) {
y = x;
if (x->key > n->key) {
x = x->lchild;
}
else if (x->key < n->key) {
x = x->rchild;
}
else {
return;
}
}
if (y != t->nil) {
if (y->key > n->key) {
y->lchild = n;
}
else {
y->rchild = n;
}
}
else {
t->root = n;
}
n->parent = y;
rbtree_insert_fixup(t, n);
}
RBTreeNode* rbtree_find(RBTree* t, KeyType key) {
RBTreeNode* cur = t->root;
while (cur != t->nil) {
if (cur->key > key) {
cur = cur->lchild;
}
else if (cur->key < key) {
cur = cur->rchild;
}
else {
return cur;
}
}
return NULL;
}
RBTreeNode* rbtree_suffix(RBTree* t, RBTreeNode* n) {
RBTreeNode* ret = n->rchild;
RBTreeNode* left = ret->lchild;
while (left != t->nil) {
ret = left;
left = left->lchild;
}
return ret;
}
RBTreeNode* rbtree_sibling(RBTreeNode* n) {
RBTreeNode* tmp = n->parent;
return n == n->parent->lchild ? n->parent->rchild : n->parent->lchild;
}
/*
* cur是实际删除节点的儿子
* 旋转要更新兄弟节点
*/
void rbtree_delete_fixup(RBTree* t, RBTreeNode* cur) {
while (cur != t->root && BLACK == cur->color) {
RBTreeNode* s = rbtree_sibling(cur);
if (cur == cur->parent->lchild) {
/*
* 情况2 兄弟节点是红色
* 父节点P改为红色,兄弟节点S改为黑色,对P左旋,兄弟节点变为旋转前的SL
* 变色旋转后的各路径黑高没有改变
* 但由于通过N节点的黑高仍少1,继续按后续情况处理
* 所以兄弟节点如果是红色则转为黑色处理
*/
if (RED == s->color) {
cur->parent->color = RED;
s->color = BLACK;
rbtree_rotate_left(t, cur->parent);
s = rbtree_sibling(cur);
}
// 以下兄弟节点S是黑色
/*
* 情况3 兄弟节点S是黑色,S的两个儿子也是黑色
* 将S变为红色,通过S的路径黑高减1,此时通过N和通过S的路径的黑高相同,
* 但是通过P的路径的黑高还是比不通过P的路径的黑高少1,所以还需要继续修复。将当前节点改为父节点P。
* 注意这里P可能是黑色也可能是红色,这两种情况统一处理了,如果P是红色,则退出循环,将当前节点变为黑色,避免破坏性质4(P,S都是红色)
*/
if (BLACK == s->lchild->color && BLACK == s->rchild->color) {
s->color = RED;
cur = cur->parent;
}
else {
/*
* 情况4 兄弟节点S是黑色,S的左儿子SL是红色,S的右儿子SR是黑色
* 交换兄弟节点S和左儿子SL的颜色,对S右旋。
* 旋转和变色后各路径的黑高和原来相同,通过N的路径还是黑高少1,通过SR的路径黑高不变,此时满足情况5,跳到情况5处理。
*/
if (RED == s->lchild->color && BLACK == s->rchild->color) {
s->color = RED;
s->lchild->color = BLACK;
rbtree_rotate_right(t, s);
s = rbtree_sibling(cur);
}
/*
* 情况5 兄弟节点S是黑色,S的右儿子SR是红色
* 父节点P和兄弟节点S互换颜色,右儿子SR变为黑色,然后对P左旋
* 修复后,原来通过N节点的路径多了个黑色节点P,黑高加1;
* 原来通过SL的路径黑高不变(P->S->SL变为S->P->SL,而SL在旋转后变为新兄弟);
* 原来通过SR的路径黑高也不变(P->S->SR变为S->SR,少了个红节点不影响,而SR在旋转后变为N的叔父)
* 此时性质5已修复。
*/
if (RED == s->rchild->color) {
s->color = cur->parent->color;
cur->parent->color = BLACK;
s->rchild->color = BLACK;
rbtree_rotate_left(t, cur->parent);
// 红黑树已修复,退出循环
cur = t->root;
}
}
}
// 与上面是镜像,互换左右孩子和左右旋即可
else {
if (RED == s->color) {
cur->parent->color = RED;
s->color = BLACK;
rbtree_rotate_right(t, cur->parent);
s = rbtree_sibling(cur);
}
if (BLACK == s->lchild->color && BLACK == s->rchild->color) {
s->color = RED;
cur = cur->parent;
}
else {
if (RED == s->rchild->color && BLACK == s->lchild->color) {
s->color = RED;
s->rchild->color = BLACK;
rbtree_rotate_left(t, s);
s = rbtree_sibling(cur);
}
if (RED == s->lchild->color) {
s->color = cur->parent->color;
cur->parent->color = BLACK;
s->lchild->color = BLACK;
rbtree_rotate_right(t, cur->parent);
cur = t->root;
}
}
}
}
/*
* 实际删除节点是黑色,儿子节点是红色时会走到这
* 情况1会走到这
* 如果当前节点是根节点或者是红色,则将颜色改为黑色
*/
cur->color = BLACK;
}
/*
* d是想要删除节点
* r是实际删除的节点
* cur是实际删除节点的儿子(当前节点)
* 找到d的后继r
*/
RBTreeNode* rbtree_delete(RBTree* t, RBTreeNode* d) {
RBTreeNode* r;
RBTreeNode* cur;
// 有两个非叶子节点则查找后继节点
if (d->lchild != t->nil && d->rchild != t->nil) {
r = rbtree_suffix(t, d);
}
else {
r = d;
}
if (r->lchild != t->nil) {
cur = r->lchild;
}
else {
cur = r->rchild;
}
// 实际删除的节点r替换为它的儿子n
if (r->parent == t->nil) {
t->root = cur;
}
else if (r == r->parent->lchild) {
r->parent->lchild = cur;
}
else {
r->parent->rchild = cur;
}
cur->parent = r->parent;
// 想要删除的节点不用实际删除,替换key,value就行
if (r != d) {
d->key = r->key;
d->value = r->value;
}
if (BLACK == r->color) {
rbtree_delete_fixup(t, cur);
}
return r;
}
void print(RBTree* t, RBTreeNode* root) {
if (root == t->nil) {
return;
}
print(t, root->lchild);
printf("%d %s\n", root->key, root->color == RED ? "红色" : "黑色");
print(t, root->rchild);
}
// main
int main() {
RBTree t;
t.nil = (RBTreeNode*)malloc(sizeof(RBTreeNode));
t.nil->color = BLACK;
t.root = t.nil;
t.nil->lchild = NULL;
t.nil->rchild = NULL;
t.nil->parent = NULL;
KeyType a[] = { 11, 32, 23, 54, 15, 66, 44, 99, 100, 8 };
int i;
int len = sizeof(a) / sizeof(KeyType);
for (i = 0; i < len; ++i) {
RBTreeNode* n = (RBTreeNode*)malloc(sizeof(RBTreeNode));
n->key = a[i];
n->value = NULL;
n->lchild = t.nil;
n->rchild = t.nil;
rbtree_insert(&t, n);
}
printf("插入后:\n");
print(&t, t.root);
printf("\n");
//printf("删除前:\n");
for (i = 0; i < len /*/ 2*/; ++i) {
RBTreeNode* n = rbtree_find(&t, a[i]);
if (n) {
printf("删除%d\n", a[i]);
RBTreeNode* d = rbtree_delete(&t, n);
if (d) {
free(d);
}
print(&t, t.root);
printf("\n");
}
}
printf("删除后:\n");
print(&t, t.root);
free(t.nil);
t.nil = NULL;
system("pause");
return 0;
}