紅黑樹的構建以及插入和刪除操作(C語言完整)
阿新 • • 發佈:2019-01-08
參照演算法導論虛擬碼。
註釋沒有很詳細,建議先看演算法導論或者其他博主的分析搞清楚insert和delete操作的方法。
#include<stdio.h>
#include<stdlib.h>
typedef int type;
typedef enum Color //定義紅黑樹結點顏色顏色型別
{
red = 0,
black = 1
}Color;
typedef struct rbtree //定義紅黑樹的結構
{
Color color;
type key;
struct rbtree *left;
struct rbtree *right;
struct rbtree *parent;
}node, *tree;
node *nil = NULL; //建立一個哨兵結點。這裡要用哨兵去讓空的葉子結點的顏色是black的,參照紅黑樹的定義
node* create(type key, node *left, node *right, node *parent) //建立結點
{
node *p;
p = (node*)malloc(sizeof(node));
p->color = black; //預設顏色為black
p->left = left;
p->right = right;
p->parent = parent;
p->key = key;
/* printf("創造結點完畢");*/
return p;
}
void left_rotate(tree &t, node *x) //左旋,這個看圖就能理解具體操作
{
node *y = x->right;
x->right = y->left;
if (y->left != nil)
y->left-> parent = x;
y->parent = x->parent;
if (x->parent == nil)
t = y;
else
{
if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
void right_rotate(tree &t, node *x) //右旋
{
node *y = x->left;
x->left = y->right;
if (y->right != nil)
y->right->parent = x;
y->parent = x->parent;
if (x->parent == nil)
t = y; //
else
{
if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
}
y->right = x;
x->parent = y;
}
node* search(tree &t, type key) //查詢元素
{
node *x = t;
if (x == nil || x->key == key)
{
return t;
}
if (key < x->key)
{
return search(x->left, key);
}
else
{
return search(x->right, key);
}
} //遞迴查詢
void rb_insert_fixup(tree &t, node* z)
{
node *y;
while ((z->parent != nil) && (z->parent->color == red)) //only when z's parent is red there will probably obey the red node has two black nodes
{
if (z->parent == z->parent->parent->left) //z'parent is the left node
{
y = z->parent->parent->right; //let y be z's uncle node
if (y->color == red) //parent and uncle and itself are all red
{
z->parent->color = black;
y->color = black;
z->parent->parent->color = red;
z = z->parent->parent; //let z up to its grandpa
}
else
{
if (z == z->parent->right) // z is a right node
{
z = z->parent;
left_rotate(t, z);
}
z->parent->color = black;
z->parent->parent->color = red;
right_rotate(t, z->parent->parent);
}
}
else //z's parent is the right node
{
y = z->parent->parent->left;
if (y->color == red)
{
z->parent->color = black;
y->color = black;
z->parent->parent->color = red;
z = z->parent->parent;
}
else
{
if (z == z->parent->left) // z is a left node
{
z = z->parent;
right_rotate(t, z);
}
z->parent->color = black;
z->parent->parent->color = red;
left_rotate(t, z->parent->parent);
}
}
}
t->color = black; //始終保持根節點是黑色
}
node* rb_insert(tree &t, node *z) //插入操作
{
if (t == NULL) //t是空的樹
{
t = (tree)malloc(sizeof(node));
nil = (node*)malloc(sizeof(node)); //初始化哨兵結點
nil->color = black;
t->left = nil;
t->right = nil;
t->parent = nil;
t->key = z->key;
t->color = black;
}
else
{
node *y = nil;
node *x = t; //
while (x != nil)
{
y = x;
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
if (y == nil)
t = z;
else
{
if (z->key < y->key)
y->left = z;
else
y->right = z;
}
z->left = nil;
z->right = nil;
z->color = red; //始終保持插入的結點是紅色,不符合具體性質了再用fixup調整
rb_insert_fixup(t, z);
}
return t;
}
node* rb_insert_(tree &t, type k)
{
node *z;
z = create(k, NULL, NULL, NULL);
return rb_insert(t, z);
}
node* min(tree &m) //最小值
{
node *n = m;
if (n == nil)
return nil;
while (n->left != nil)
{
n = n->left;
}
return n;
}
node* successor(node *s) //後繼
{
node *p;
if (s->right != nil) //結點右子樹非空
{
return min(s->right);
}
else
{
p = s->parent; //結點是左孩子,後繼就是他的父節點
while (p != nil && p->right == s)
{ //結點是右孩子,向上查詢,直到遇到一個有左孩子的父節點,那就是後繼
{
s = p;
p = p->parent;
}
}
return p;
}
}
void rb_delete_fixup(tree &t, node *x)
{
node *w;
while (x != t && x->color == black) //
{
if (x == x->parent->left)
{
w = x->parent->right;
if (w->color == red)
{
w->color = black;
x->parent->color = red;
left_rotate(t, x->parent);
w = x->parent->right;
}
if (w->left->color == black && w->right->color == black)
{
w->color = red;
x = x->parent;
}
else if (w->right->color == black)
{
w->left->color = black;
w->color = red;
right_rotate(t, w);
w = x->parent->right;
}
w->color = x->parent->color;
x->parent->color = black;
w->right->color = black;
left_rotate(t, x->parent);
x = t; //
}
else
{
w = x->parent->left;
if (w->color = red)
{
w->color = black;
x->parent->color = red;
right_rotate(t, x->parent);
w = x->parent->left;
}
if (w->left->color == black && w->right->color == black)
{
w->color = red;
x = x->parent;
}
else if (w->left->color == black)
{
w->right->color = black;
w->color = red;
left_rotate(t, w);
w = x->parent->right;
}
w->color = x->parent->color;
x->parent->color = black;
w->left->color= black;
right_rotate(t, x->parent);
x = t; //
}
}
x->color = black;
}
node* rb_delete(tree &t, node *z)
{
node *y, *x;
node *m=nil;
node* n = z;
if (z->left == nil || z->right == nil)
y = z;
else
y = successor(n); //這一行使z的右孩子發生變化
if (y->left != nil)
x = y->left;
else
x = y->right;
x->parent = y->parent;
if (y->parent == nil)
t = x; //
else
{
if (y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
}
if (y != z)
{
z->key = (y->key);
}
if (y->color == black)
rb_delete_fixup(t, x);
return t;
}
node* rb_delete_(tree &t, type k)
{
node *z;
z = search(t, k);
if (z != nil)
{
t = rb_delete(t, z);
}
else
printf("無此元素");
return t;
}
void print_tree(tree &t) //中序遍歷列印
{
if (t != nil && t != NULL)
{
print_tree(t->left);
printf("%7d , %5d\n ", t->key, t->color);
print_tree(t->right);
}
}
int main()
{
int i;
tree zz = NULL, mm = NULL, nn = NULL, xx = NULL, yy = NULL;
type k;
int a[12] = {3,12,15,17,19,55,20,18,36,48,31,29 };
printf("\n原來的數字是:----------------------------------------\n");
for (i = 0; i<12; i++)
{
printf("%d ", a[i]);
zz = rb_insert_(zz, a[i]);
}
printf("\n中序遍歷是:-------------------------------------------\n");
printf("規定:red=0,black=1\n");
print_tree(zz);
printf("\n要刪除的值是:---------------------------------------");
scanf_s("%d", &k);
yy = rb_delete_(zz, k);
print_tree(yy);
printf("\n要插入的值是:--------------------------------------");
scanf_s("%d", &k);
rb_insert_(zz, k);
print_tree(zz);
}
過程中遇到的問題:
1.開始不知道有列舉型別,不知道怎麼定義color,用了#define結果總是報錯
2.開始沒有使用哨兵元素,會導致在逐漸插入的過程中,檢驗父節點,祖父節點或叔叔結點可能碰到空的時候那個NULL是沒有顏色的,執行到這裡就中斷了。所以必須事先定義好哨兵nil。
3.演算法導論上的虛擬碼結構不算清晰,就是if else 那些巢狀寫的不太清楚,得理清楚邏輯結構了才能自己寫對。
4.導論上的地方省略了結構相似的部分,x == x->parent->left,->right就省略了,看起來不需要動腦子,其實還是容易寫錯的
5.學習方式應該是有問題的,先看的演算法導論,把思路理得清楚一些了,然後看的網上的解析更明白一些了,但是還是體現在被動的去看懂。然後再去根據導論的虛擬碼實現,結果又很多bug,改了一些,然後又找了紅黑樹的現成程式碼參照修改,把它改出來了。但是這樣學習感覺還是很被動的再接受,從翻譯虛擬碼到debug,其實進步並不大,現在要自己不參照虛擬碼寫出來還是不行。甚至不看書複述紅黑樹的基本思想也不能完整想出來。所以這樣是有很多問題的,今後要改進。。。