轉載:http://blog.csdn.net/v_JULY_v/article/details/6114226
前言:
紅黑樹作爲一種經典而高級的數據結構,相信,已經被不少人實現過,但不是因爲程序不夠完善而無法運行,就是因爲程序完全沒有註釋,初學者根本就看不懂。
此份紅黑樹的c源碼最初從linux-lib-rbtree.c而來,後經一網友那誰(http://www.cppblog.com/converse/)用c寫了出來。在此,向原作者表示敬意。但原來的程序存在沒有任何一行註釋。沒有一行註釋的程序,令程序的價值大打折扣。
所以,我特把這份源碼放到了windows xp+vc 6.0上,一行一行的完善,一行一行的給它添加註釋,至此,紅黑樹c帶註釋的源碼,就擺在了您眼前,有不妥、不正之處,還望不吝指正。
------------
紅黑樹的六篇文章:
-------------------------
ok,咱們開始吧。
相信,經過我前倆篇博文對紅黑樹的介紹,你應該對紅黑樹有了透徹的理解了(沒看過的朋友,可事先查上面的倆篇文章,或與此文的源碼剖析對應着看)。
本套源碼剖析把重點放在紅黑樹的3種插入情況,與紅黑樹的4種刪除情況。其餘的能從略則儘量簡略。
目錄:
一、左旋代碼分析
二、右旋
三、紅黑樹查找結點
四、紅黑樹的插入
五、紅黑樹的3種插入情況
六、紅黑樹的刪除
七、紅黑樹的4種刪除情況
八、測試用例
好的,咱們還是先從樹的左旋、右旋代碼,開始(大部分分析,直接給註釋):
- //一、左旋代碼分析
- /*-----------------------------------------------------------
- | 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;
- }
- //二、右旋
- /*-----------------------------------------------------------
- | 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;
- }
- //三、紅黑樹查找結點
- //----------------------------------------------------
- //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);
- }
- //四、紅黑樹的插入
- //---------------------------------------------------------
- //紅黑樹的插入結點
- 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修復紅黑樹
- 的性質
- }
- //五、紅黑樹的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; //返回根結點。
- }
- //六、紅黑樹的刪除
- //------------------------------------------------------------
- //紅黑樹的刪除結點
- 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;
- }
- //七、紅黑樹的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
- }
- //八、測試用例
- //主函數
- 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;
- }
ok,完。
後記:
一、歡迎任何人就此份源碼,以及我的前述倆篇文章,進行討論、提議。
但任何人,引用此份源碼剖析,必須得註明作者本人July以及出處。
紅黑樹系列,已經寫了三篇文章,相信,教你透徹瞭解紅黑樹的目的,應該達到了。
二、本文完整源碼,請到此處下載: