1. 程式人生 > >紅黑樹的構建以及插入和刪除操作(C語言完整)

紅黑樹的構建以及插入和刪除操作(C語言完整)

參照演算法導論虛擬碼。
註釋沒有很詳細,建議先看演算法導論或者其他博主的分析搞清楚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,其實進步並不大,現在要自己不參照虛擬碼寫出來還是不行。甚至不看書複述紅黑樹的基本思想也不能完整想出來。所以這樣是有很多問題的,今後要改進。。。