前言 node
紅黑樹做爲一種經典而高級的數據結構,相信已經被很多人實現過,可是由於程序不夠完善而沒法運行,就是由於程序徹底沒有註釋,初學者根本就看不懂。——這句話相對贊
linux
此份紅黑樹的C源碼最初從linux-lib-rbtree.c而來,後經一網友那誰(http://www.cppblog.com/converse/)用C寫了出來。在此,向原做者表示敬意。但原來的程序沒有任何一行註釋。沒有一行註釋的程序,令程序的價值大打折扣。
數據結構
因此,特把這份源代碼在VC6.0上,一行一行的完善,一行一行的給它添加註釋。至此,紅黑樹C帶註釋源碼,就擺在了您的眼前,若有不妥、不正之處,還望不吝指正。
函數
1、紅黑樹C語言源碼實現 測試
目錄:
1、左旋代碼分析
2、右旋
3、紅黑樹查找結點
4、紅黑樹的插入
5、紅黑樹的3種插入狀況
6、紅黑樹的刪除
7、紅黑樹的4種刪除狀況
8、測試用例
spa
//1、左旋代碼分析 /*----------------------------------------------------------- | node right | / / ==> / / | a right node y | / / / / | b y a b //左旋 -----------------------------------------------------------*/ static rb_node_t* rb_rotate_left(rb_node_t* node, rb_node_t* root) { rb_node_t* right = node->right; //指定指針指向 right<--node->right if ((node->right = right->left)) { right->left->parent = node; //比如上面的註釋圖,node成爲b的父母 } right->left = node; //node成爲right的左孩子 if ((right->parent = node->parent)) { if (node == node->parent->right) { node->parent->right = right; } else { node->parent->left = right; } } else { root = right; } node->parent = right; //right成爲node的父母 return root; } //2、右旋 /*----------------------------------------------------------- | node left | / / / / | left y ==> a node | / / / / | a b b y //右旋與左旋差很少,分析略過 -----------------------------------------------------------*/ static rb_node_t* rb_rotate_right(rb_node_t* node, rb_node_t* root) { rb_node_t* left = node->left; if ((node->left = left->right)) { left->right->parent = node; } left->right = node; if ((left->parent = node->parent)) { if (node == node->parent->right) { node->parent->right = left; } else { node->parent->left = left; } } else { root = left; } node->parent = left; return root; } //3、紅黑樹查找結點 //---------------------------------------------------- //rb_search_auxiliary:查找 //rb_node_t* rb_search:返回找到的結點 //---------------------------------------------------- static rb_node_t* rb_search_auxiliary(key_t key, rb_node_t* root, rb_node_t** save) { rb_node_t *node = root, *parent = NULL; int ret; while (node) { parent = node; ret = node->key - key; if (0 < ret) { node = node->left; } else if (0 > ret) { node = node->right; } else { return node; } } if (save) { *save = parent; } return NULL; } //返回上述rb_search_auxiliary查找結果 rb_node_t* rb_search(key_t key, rb_node_t* root) { return rb_search_auxiliary(key, root, NULL); } //4、紅黑樹的插入 //--------------------------------------------------------- //紅黑樹的插入結點 rb_node_t* rb_insert(key_t key, data_t data, rb_node_t* root) { rb_node_t *parent = NULL, *node; parent = NULL; if ((node = rb_search_auxiliary(key, root, &parent))) //調用rb_search_auxiliary找到插入結 點的地方 { return root; } node = rb_new_node(key, data); //分配結點 node->parent = parent; node->left = node->right = NULL; node->color = RED; if (parent) { if (parent->key > key) { parent->left = node; } else { parent->right = node; } } else { root = node; } return rb_insert_rebalance(node, root); //插入結點後,調用rb_insert_rebalance修復紅黑樹 的性質 } //5、紅黑樹的3種插入狀況 //接下來,我們重點分析針對紅黑樹插入的3種狀況,而進行的修復工做。 //-------------------------------------------------------------- //紅黑樹修復插入的3種狀況 //爲了在下面的註釋中表示方便,也爲了讓下述代碼與個人倆篇文章相對應, //用z表示當前結點,p[z]表示父母、p[p[z]]表示祖父、y表示叔叔。 //-------------------------------------------------------------- static rb_node_t* rb_insert_rebalance(rb_node_t *node, rb_node_t *root) { rb_node_t *parent, *gparent, *uncle, *tmp; //父母p[z]、祖父p[p[z]]、叔叔y、臨時結點*tmp while ((parent = node->parent) && parent->color == RED) { //parent 爲node的父母,且當父母的顏色爲紅時 gparent = parent->parent; //gparent爲祖父 if (parent == gparent->left) //當祖父的左孩子即爲父母時。 //其實上述幾行語句,無非就是理順孩子、父母、祖父的關係。:D。 { uncle = gparent->right; //定義叔叔的概念,叔叔y就是父母的右孩子。 if (uncle && uncle->color == RED) //狀況1:z的叔叔y是紅色的 { uncle->color = BLACK; //將叔叔結點y着爲黑色 parent->color = BLACK; //z的父母p[z]也着爲黑色。解決z,p[z]都是紅色的問題。 gparent->color = RED; node = gparent; //將祖父當作新增結點z,指針z上移倆層,且着爲紅色。 //上述狀況1中,只考慮了z做爲父母的右孩子的狀況。 } else //狀況2:z的叔叔y是黑色的, { if (parent->right == node) //且z爲右孩子 { root = rb_rotate_left(parent, root); //左旋[結點z,與父母結點] tmp = parent; parent = node; node = tmp; //parent與node 互換角色 } //狀況3:z的叔叔y是黑色的,此時z成爲了左孩子。 //注意,1:狀況3是由上述狀況2變化而來的。 //......2:z的叔叔老是黑色的,不然就是狀況1了。 parent->color = BLACK; //z的父母p[z]着爲黑色 gparent->color = RED; //原祖父結點着爲紅色 root = rb_rotate_right(gparent, root); //右旋[結點z,與祖父結點] } } else { // if (parent == gparent->right) 當祖父的右孩子即爲父母時。(解釋請看本文評論下第23樓,同時,感謝SupremeHover指正!) uncle = gparent->left; //祖父的左孩子做爲叔叔結點。[原理仍是與上部分同樣的] if (uncle && uncle->color == RED) //狀況1:z的叔叔y是紅色的 { uncle->color = BLACK; parent->color = BLACK; gparent->color = RED; node = gparent; //同上。 } else //狀況2:z的叔叔y是黑色的, { if (parent->left == node) //且z爲左孩子 { root = rb_rotate_right(parent, root); //以結點parent、root右旋 tmp = parent; parent = node; node = tmp; //parent與node 互換角色 } //通過狀況2的變化,成爲了狀況3. parent->color = BLACK; gparent->color = RED; root = rb_rotate_left(gparent, root); //以結點gparent和root左旋 } } } root->color = BLACK; //根結點,不論怎樣,都得置爲黑色。 return root; //返回根結點。 } //6、紅黑樹的刪除 //------------------------------------------------------------ //紅黑樹的刪除結點 rb_node_t* rb_erase(key_t key, rb_node_t *root) { rb_node_t *child, *parent, *old, *left, *node; color_t color; if (!(node = rb_search_auxiliary(key, root, NULL))) //調用rb_search_auxiliary查找要刪除的 結點 { printf("key %d is not exist!/n"); return root; } old = node; if (node->left && node->right) { node = node->right; while ((left = node->left) != NULL) { node = left; } child = node->right; parent = node->parent; color = node->color; if (child) { child->parent = parent; } if (parent) { if (parent->left == node) { parent->left = child; } else { parent->right = child; } } else { root = child; } if (node->parent == old) { parent = node; } node->parent = old->parent; node->color = old->color; node->right = old->right; node->left = old->left; if (old->parent) { if (old->parent->left == old) { old->parent->left = node; } else { old->parent->right = node; } } else { root = node; } old->left->parent = node; if (old->right) { old->right->parent = node; } } else { if (!node->left) { child = node->right; } else if (!node->right) { child = node->left; } parent = node->parent; color = node->color; if (child) { child->parent = parent; } if (parent) { if (parent->left == node) { parent->left = child; } else { parent->right = child; } } else { root = child; } } free(old); if (color == BLACK) { root = rb_erase_rebalance(child, parent, root); //調用rb_erase_rebalance來恢復紅黑樹性 質 } return root; } //7、紅黑樹的4種刪除狀況 //---------------------------------------------------------------- //紅黑樹修復刪除的4種狀況 //爲了表示下述註釋的方便,也爲了讓下述代碼與個人倆篇文章相對應, //x表示要刪除的結點,*other、w表示兄弟結點, //---------------------------------------------------------------- static rb_node_t* rb_erase_rebalance(rb_node_t *node, rb_node_t *parent, rb_node_t *root) { rb_node_t *other, *o_left, *o_right; //x的兄弟*other,兄弟左孩子*o_left,*o_right while ((!node || node->color == BLACK) && node != root) { if (parent->left == node) { other = parent->right; if (other->color == RED) //狀況1:x的兄弟w是紅色的 { other->color = BLACK; parent->color = RED; //上倆行,改變顏色,w->黑、p[x]->紅。 root = rb_rotate_left(parent, root); //再對p[x]作一次左旋 other = parent->right; //x的新兄弟new w 是旋轉以前w的某個孩子。其實就是左旋後 的效果。 } if ((!other->left || other->left->color == BLACK) && (!other->right || other->right->color == BLACK)) //狀況2:x的兄弟w是黑色,且w的倆個孩子也 都是黑色的 { //因爲w和w的倆個孩子都是黑色的,則在x和w上得去掉一黑色, other->color = RED; //因而,兄弟w變爲紅色。 node = parent; //p[x]爲新結點x parent = node->parent; //x<-p[x] } else //狀況3:x的兄弟w是黑色的, { //且,w的左孩子是紅色,右孩子爲黑色。 if (!other->right || other->right->color == BLACK) { if ((o_left = other->left)) //w和其左孩子left[w],顏色交換。 { o_left->color = BLACK; //w的左孩子變爲由黑->紅色 } other->color = RED; //w由黑->紅 root = rb_rotate_right(other, root); //再對w進行右旋,從而紅黑性質恢復。 other = parent->right; //變化後的,父結點的右孩子,做爲新的兄弟結點 w。 } //狀況4:x的兄弟w是黑色的 other->color = parent->color; //把兄弟節點染成當前節點父節點的顏色。 parent->color = BLACK; //把當前節點父節點染成黑色 if (other->right) //且w的右孩子是紅 { other->right->color = BLACK; //兄弟節點w右孩子染成黑色 } root = rb_rotate_left(parent, root); //並再作一次左旋 node = root; //並把x置爲根。 break; } } //下述狀況與上述狀況,原理一致。分析略。 else { other = parent->left; if (other->color == RED) { other->color = BLACK; parent->color = RED; root = rb_rotate_right(parent, root); other = parent->left; } if ((!other->left || other->left->color == BLACK) && (!other->right || other->right->color == BLACK)) { other->color = RED; node = parent; parent = node->parent; } else { if (!other->left || other->left->color == BLACK) { if ((o_right = other->right)) { o_right->color = BLACK; } other->color = RED; root = rb_rotate_left(other, root); other = parent->left; } other->color = parent->color; parent->color = BLACK; if (other->left) { other->left->color = BLACK; } root = rb_rotate_right(parent, root); node = root; break; } } } if (node) { node->color = BLACK; //最後將node[上述步驟置爲了根結點],改成黑色。 } return root; //返回root } //8、測試用例 //主函數 int main() { int i, count = 100; key_t key; rb_node_t* root = NULL, *node = NULL; srand(time(NULL)); for (i = 1; i < count; ++i) { key = rand() % count; if ((root = rb_insert(key, i, root))) { printf("[i = %d] insert key %d success!/n", i, key); } else { printf("[i = %d] insert key %d error!/n", i, key); exit(-1); } if ((node = rb_search(key, root))) { printf("[i = %d] search key %d success!/n", i, key); } else { printf("[i = %d] search key %d error!/n", i, key); exit(-1); } if (!(i % 10)) { if ((root = rb_erase(key, root))) { printf("[i = %d] erase key %d success/n", i, key); } else { printf("[i = %d] erase key %d error/n", i, key); } } } return 0; }