學習之旅-紅黑樹之刪除
前人栽樹,後人乘涼。關於這樣紅黑樹的帖子已經很多。我就不再重複造輪子了。我自己發現一個刪除說的很好的部落格,連結:http://gengning938.blog.163.com/blog/static/1282253812011420103852696/
我只是說說自己的理解。方便以後自己回來忘了的時候,能很快看懂。
分為幾種情況:
1、刪除的是葉子節點。那麼,如果是紅色,直接刪除。如果是黑色,則這條分支少了一個黑色節點。需要調整。
2、刪除的不是葉子節點,那麼從他的右子樹中找一個最小的節點和他交換,然後要刪除的點就移到了葉子節點,回到情況1。
那麼這個時候回到情況1了。這個運氣好是紅色的。但是如果是刪除黑色的點呢?比如刪除根節點13 。
如果刪除的是13,那麼右子樹最小的是15,交換13和15。然後刪除位置變為15。這個時候要刪除的是黑色的節點,那就複雜了。又可以分為下面幾種情況
1、刪除的點的兄弟是紅色,那麼他的父親,孩子都是黑色。這一種,交換兄弟與父親的顏色,然後以父節點為準,旋轉樹,旋轉之後新的兄弟變成了黑色。。轉到下一種情況
2、刪除的點的兄弟是黑色。又可以分為幾種情況:
1)、黑色的兄弟的孩子都是黑色,那麼把兄弟染成紅色,這樣,兄弟子樹和自己都少了一個黑色節點,兄弟子樹和自己平衡了。但是整個子樹又比其他子樹少了一個黑色,所以需要看父親節點是紅色還是黑色,如果父親是紅色,直接染黑就完全平衡了,但是父親節點如果是黑色,那就繼續看父親的兄弟顏色。。
2)、黑色的兄弟孩子有紅色的。那麼如果遠親侄子是紅色,那麼遠親侄子染成黑色,交換父親與兄弟的顏色,然後以父親為準,根據刪除點是父親的左孩子還是右孩子進行相應的旋轉,如果是左孩子就左旋,如果是右孩子就右旋。最後平衡。
3)、如果黑色的兄弟的遠親侄子是紅色,那就轉換為2)的情況。
所以複雜的情況就是四種,第1種,和第二種的三種情況。
以下是程式碼,寫的時候參照了一下STL :
void tst_rbt_delete(tst_rbtree* tree, tst_rbtnode* node) { tst_rbtnode **root, *subt, *temp, *sentinel, *w; root = &tree->root; sentinel = tree->sentinel; /*二叉樹的刪除*/ //找到要刪除的節點。 if (sentinel == node->lchild) //fd1 { subt = node->rchild; temp = node; } else if (sentinel == node->rchild) //fd2 { subt = node->lchild; temp = node; } else //fd3 { //node的左右孩子都不為空,那麼從右子樹中找到key最小的節點,交換node和這個點,再刪除node temp = tst_rbt_min_node(node->rchild, sentinel); if (temp->lchild == sentinel) { //既然是最小的節點,其實並不會進入這個分支,如果還有左孩子,肯定不是最小的 subt = temp->rchild; } else { subt = temp->lchild; } } //如果要刪除的點是根節點,這種情況只會是fd1,fd2中的一種 if (temp == *root) { *root = subt; tst_rbt_set_black(subt); tst_rbt_node_reset(node); return; } unsigned char isRed = tst_rbt_is_red(temp); //把找到的節點移出樹外。孩子上提 if (tst_rbt_is_lchild(temp)) { tst_rbt_parent(temp)->lchild = subt; } else { tst_rbt_parent(temp)->rchild = subt; } if (temp == node) { //如果要刪除的點就是node,那麼node已經被移出樹外 subt->parent = temp->parent; } else { //如果要刪除的點不是node,而是右子樹中最小的節點 if (node == tst_rbt_parent(temp)) { //如果node是temp的父節點,那麼,subt的父節點還是temp,但是如果subt是sentinel,那麼sentinel一開始parent是NULL subt->parent = temp; } else { //如果不是,那麼subt的父節點該是temp的父節點 subt->parent = temp->parent; } temp->parent = node->parent; temp->lchild = node->lchild; temp->rchild = node->rchild; tst_rbt_copy_color(temp, node); if (node == *root) { //如果node是根,設定根為找到的節點 *root = temp; } else { //把找到的點替換node if (tst_rbt_is_lchild(node)) { tst_rbt_parent(node)->lchild = temp; } else { tst_rbt_parent(node)->rchild = temp; } } if (!tst_rbt_is_leaf(tree, temp->lchild)) { temp->lchild->parent = temp; } if (!tst_rbt_is_leaf(tree, temp->rchild)) { temp->rchild->parent = temp; } } tst_rbt_node_reset(node); if (isRed) { return; } //dsptree(*root, sentinel);//列印樹 /* 重新平衡紅黑樹 */ while (subt != *root && tst_rbt_is_black(subt)) { if (tst_rbt_is_lchild(subt)) { w = tst_rbt_parent(subt)->rchild; /*複雜情況1: *兄弟是紅色,那麼父節點,兄弟的孩子都是黑色。交換兄弟與父親的顏色 *然後左旋。轉換情況為要刪除的點的兄弟是黑色。 */ if (tst_rbt_is_red(w))//w是紅色。則w的parent,child必然是黑色 { tst_rbt_set_red(tst_rbt_parent(subt)); tst_rbt_set_black(w); tst_rbt_rotate_left(root, tst_rbt_parent(subt), sentinel); w = tst_rbt_parent(subt)->rchild;//轉化為subt的兄弟是黑色的情況 } /*複雜情況2: *如果要刪除的點的兄弟不是紅色,並且兄弟的左右孩子都是黑色 *那麼把兄弟染成紅色,再看父節點。如果是紅色,跳出迴圈,最後染成黑色,ok *如果父節點是黑色,那麼又回到最原始的問題(情況1234都可能) */ if (tst_rbt_is_black(w->lchild) && tst_rbt_is_black(w->rchild)) { tst_rbt_set_red(w); subt = tst_rbt_parent(subt); } else { /*複雜情況3:兄弟節點有紅色孩子 *如果兄弟的遠侄子是黑色,那麼肯定近親侄子是紅色。 *最終目的是把遠侄子變成紅色,轉換成下一種情況,可以最終解決問題。 */ if (tst_rbt_is_black(w->rchild)) { tst_rbt_set_black(w->lchild); tst_rbt_set_red(w); tst_rbt_rotate_right(root, w, sentinel); w = tst_rbt_parent(subt)->rchild; } /*複雜情況4: *如果遠侄子是紅色,直接把遠侄子設為黑色,交換父親與兄弟顏色 *最終選擇合適的旋轉方向旋轉子樹。達到平衡。 */ tst_rbt_copy_color(w, tst_rbt_parent(subt)); tst_rbt_set_black(tst_rbt_parent(subt)); tst_rbt_set_black(w->rchild); tst_rbt_rotate_left(root, tst_rbt_parent(subt), sentinel); subt = *root;//結束迴圈,同時方便後續設定根節點為黑色 } } else { //與上面,刪除點是父親的左孩子相似,只不過,旋轉樹的時候方向與上面情況相反 w = tst_rbt_parent(subt)->lchild; if (tst_rbt_is_red(w))//w是紅色。w的parent,child必然是黑色 { tst_rbt_set_black(w); tst_rbt_set_red(tst_rbt_parent(subt)); tst_rbt_rotate_right(root, tst_rbt_parent(subt), sentinel); w = tst_rbt_parent(subt)->lchild; } if (tst_rbt_is_black(w->lchild) && tst_rbt_is_black(w->rchild)) { tst_rbt_set_red(w); subt = tst_rbt_parent(subt); } else { if (tst_rbt_is_black(w->lchild)) { tst_rbt_set_red(w); tst_rbt_set_black(w->rchild); tst_rbt_rotate_left(root, w, sentinel); w = tst_rbt_parent(subt)->lchild; } tst_rbt_copy_color(w, tst_rbt_parent(subt)); tst_rbt_set_black(tst_rbt_parent(subt)); tst_rbt_set_black(w->lchild); tst_rbt_rotate_right(root, tst_rbt_parent(subt), sentinel); subt = *root; } } //dsptree(*root, sentinel);//列印樹 } tst_rbt_set_black(subt); }