在《算法導論 之 紅黑樹 - 插入》中已經對紅黑樹的5個性質做了較詳細的分析,同時也給出了insert操作的C語言實現。首先我們再回顧一下紅黑樹的5個性質:
①、每個節點要麼是紅色的,要麼是黑色的;
②、根結點是黑色的;
③、所有葉子結點(NIL)都是黑色的;
④、如果一個結點是紅色,則它的兩個兒子都是黑色的;
⑤、對任何一個結點,從該結點通過其子孫結點到達葉子結點(NIL)的所有路徑上包含相同數目的黑結點。
和插入操作一樣,結點的刪除操作的時間復雜度也是O(log2@N)[注:以2為底數,N為對數],但刪除操作的處理更復雜一些。
假如需要刪除結點D,則刪除操作的過程有如下幾種情況:[注:在以下所有繪制的紅黑樹中,均未繪制葉子結點]
情況1:被刪結點D的左孩子為葉子結點,右孩子無限制(可為葉子結點,也可為非葉子結點)
處理過程:刪除結點D,並用右孩子結點替代結點D的位置。如果被刪結點D為紅色,則紅黑樹性質未被破壞,因此無需做其他調整;如果被刪結點D為黑色,則需進一步做調整處理。
圖1 情況1-1:左右孩子均為葉子結點<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+W9K219O94bXjyKG0+sHLveG140S1xM671sNdPC9wPgoKPGJyPgoKPGltZyBzcmM9"/uploadfile/Collfiles/20140118/20140118090313114.jpg" alt="\">
圖2 情況1-2:左孩子為葉子結點 右孩子不為葉子結點
[結點DR取代了葉子結點的位置]
情況2: 被刪結點D的右孩子為葉子結點,左孩子不為葉子結點
處理過程:刪除結點D,並用左孩子節點替代結點D的位置。如果被刪結點D為紅色,則紅黑樹性質未被破壞,因此不需做其他調整;如果被刪結點D為黑色, 則需進一步做調整處理。
圖3 情況2:右孩子為葉子結點 左孩子不為葉子結點
[結點DL取代結點D的位置]
情況3: 被刪結點D的左右孩子均不為葉子節點
處理過程:找到結點D的後繼結點S,將結點S的key值賦給結點D,再將結點S從樹中刪除,並用結點S的右孩子替代結點S的位置。從前面的描述可以看出,其實被刪的是結點D的後繼結點S。如果被刪結點S為紅色,則紅黑樹性質未被破壞,因此不需做其他調整;如果被刪結點S為黑色,則需進一步做調整處理。
圖4 情況3:左右孩子均不為葉子結點
[後繼結點的右孩子SR取代後繼結點S的位置]
綜合情況1、2、3可知,當實際被刪的結點為黑色時,才需進一步做調整處理 —— 實際被刪的結點為紅色時,並不會破壞紅黑樹的5點性質。
當紅黑樹中實際被刪除的結點為黑色時,則可能破壞紅黑樹的5個性質。經過分析總結,破壞紅黑樹性質的情況有如下幾種:
============================================================================
前提1:參照結點N為父結點P的左孩子
============================================================================
情況1):參照結點N的兄弟B是紅色的
處理過程:首先,將父結點P的顏色改為紅色,兄弟結點的顏色改為黑色;其次,以父結點P為支點進行左旋處理 ——情況1轉變為情況2或3、4。如下圖所示:[注意:請注意圖中處理前後node、brother指針的變化,這將是後續處理的參照]
圖5 調整情況1
情況2):參照結點N的兄弟B是黑色的,且B的兩個孩子都是黑色的
處理過程:將兄弟結點B的顏色改為紅色 ——情況2處理完成後,不必再進行情況3、4的判斷,重新判斷前提1、2。如下圖所示:[注意:請注意圖中處理前後node、brother指針的變化,這將是後續處理的參照]
圖6 調整情況2
情況3):參照結點N的兄弟B是黑色的,且B的左孩子是紅色的,右孩子是黑色的
處理過程:首先,將兄弟結點B的顏色改為紅色,結點B的左孩子改為黑色;其次,以結點B為支點進行右旋處理 ——情況3轉化為情況4,後續必須進行情況4的處理。如下圖所示:[注意:請注意圖中處理前後node、brother指針的變化,這將是後續處理的參照]
圖7 調整情況3
情況4):參照結點N的兄弟B是黑色的,且B的左孩子是黑色的,右孩子是紅色的
處理過程:首先,將父結點P的顏色拷貝給兄弟結點B,再將父結點P的顏色改為黑色,兄弟結點的顏色改為黑色;其次,以父結點P為支點,進行左旋處理;再次,將node改為樹的根結點,也意味著調整結束。如下圖所示:[注意:請注意圖中處理前後node指針的變化,這將是後續處理的參照]
圖8 調整情況4
[注:藍色表示結點顏色可能為紅,也可能為黑,在此也更能突出復制結點P的顏色給結點B]
============================================================================
前提2:參照結點N為父結點P的右孩子
============================================================================
其處理流程與前提1的四種情況正好相反,後續再補充.../****************************************************************************** **函數名稱: rbt_delete **功 能: 刪除操作(外部接口) **輸入參數: ** tree: 紅黑樹 ** key: 關鍵字 **輸出參數: NONE **返 回: RBT_SUCCESS:成功 RBT_FAILED:失敗 **實現描述: ** 通過key找到需要被刪除的結點,再調用_rbt_delete()執行刪除操作 **注意事項: **作 者: # Qifeng.zou # 2013.12.27 # ******************************************************************************/ int rbt_delete(rbt_tree_t *tree, int key) { rbt_node_t *node = tree->root; if(tree->sentinel == node) { return RBT_SUCCESS; } while(tree->sentinel != node) { if(key == node->key) { return _rb_delete(tree, node); /* 刪除結點 */ } else if(key < node->key) { node = node->lchild; } else { node = node->rchild; } } return RBT_SUCCESS; }
代碼1 刪除操作(外部接口)
/****************************************************************************** **函數名稱: rbt_delete **功 能: 刪除結點(內部接口) **輸入參數: ** tree: 紅黑樹 ** dnode: 將被刪除的結點 **輸出參數: NONE **返 回: RBT_SUCCESS:成功 RBT_FAILED:失敗 **實現描述: ** 1. 如果將被刪除的結點dnode無後繼結點,則直接被刪除,並被其左孩子或右孩子替代其位置 ** 2. 如果將被刪除的結點dnode有後繼結點,則將後繼結點的其賦給dnode,並刪除後繼結點, ** 再將後繼結點的右孩子取代後繼結點的位置 ** 3. 完成1、2的處理之後,如果紅黑樹的性質被破壞,則調用rbt_delete_fixup()進行調整 **注意事項: **作 者: # Qifeng.zou # 2013.12.28 # ******************************************************************************/ int _rb_delete(rbt_tree_t *tree, rbt_node_t *dnode) { rbt_node_t *parent = NULL, *next = NULL, *refer = NULL; /* 查找dnode的後繼結點next */ if((tree->sentinel == dnode->lchild) || (tree->sentinel == dnode->rchild)) { next = dnode; } else { next = dnode->rchild; while(tree->sentinel != next->lchild) { next = next->lchild; } } /* 設置替代後繼結點的結點refer(參考結點) */ if(tree->sentinel != next->lchild) { refer = next->lchild; } else { refer = next->rchild; } refer->parent = next->parent; if(tree->sentinel == next->parent) { tree->root = refer; } else { if(next == next->parent->lchild) { next->parent->lchild = refer; } else { next->parent->rchild = refer; } } if(next != dnode) { dnode->key = next->key; /* Copy next's satellite data into dnode */ } if(rbt_is_red(next)) /* Not black */ { free(next); return RBT_SUCCESS; } free(next); return rbt_delete_fixup(tree, refer); /* 修復紅黑樹性質 */ }
代碼2 刪除結點(內部接口)
/****************************************************************************** **函數名稱: rbt_delete_fixup **功 能: 修復刪除操作造成的黑紅樹性質的破壞(內部接口) **輸入參數: ** tree: 紅黑樹 ** node: 實際被刪結點的替代結點(注: node有可能是葉子結點) **輸出參數: NONE **返 回: RBT_SUCCESS:成功 RBT_FAILED:失敗 **實現描述: **注意事項: ** 注意: 被刪結點為黑色結點,才能調用此函數進行性質調整 **作 者: # Qifeng.zou # 2013.12.28 # ******************************************************************************/ int rbt_delete_fixup(rbt_tree_t *tree, rbt_node_t *node) { rbt_node_t *parent = NULL, *brother = NULL; while(rbt_is_black(node) && (tree->root != node)) { /* Set parent and brother */ parent = node->parent; if(node == parent->lchild) { brother = parent->rchild; /* Case 1: 兄弟結點為紅色: 以parent為支點, 左旋處理 */ if(rbt_is_red(brother)) { rbt_set_red(parent); rbt_set_black(brother); rbt_left_rotate(tree, parent); /* 參照結點node不變, 兄弟結點改為parent->rchild */ brother = parent->rchild; /* 注意: 此時處理還沒有結束,還需要做後續的調整處理 */ } /* Case 2: 兄弟結點為黑色(默認), 且兄弟結點的2個子結點都為黑色 */ if(rbt_is_black(brother->lchild) && rbt_is_black(brother->rchild)) { rbt_set_red(brother); node = parent; } else { /* Case 3: 兄弟結點為黑色(默認), 兄弟節點的左子結點為紅色, 右子結點為黑色: 以brother為支點, 右旋處理 */ if(rbt_is_black(brother->rchild)) { rbt_set_black(brother->lchild); rbt_set_red(brother); rbt_right_rotate(tree, brother); /* 參照結點node不變 */ brother = parent->rchild; } /* Case 4: 兄弟結點為黑色(默認), 兄弟結點右孩子結點為紅色: 以parent為支點, 左旋處理 */ rbt_copy_color(brother, parent); rbt_set_black(brother->rchild); rbt_set_black(parent); rbt_left_rotate(tree, parent); node = tree->root; } } else { brother = parent->lchild; /* Case 1: 兄弟結點為紅色: 以parent為支點, 右旋處理 */ if(rbt_is_red(brother)) { rbt_set_red(parent); rbt_set_black(brother); rbt_right_rotate(tree, parent); /* 參照結點node不變 */ brother = parent->lchild; /* 注意: 此時處理還沒有結束,還需要做後續的調整處理 */ } /* Case 2: 兄弟結點為黑色(默認), 且兄弟結點的2個子結點都為黑色 */ if(rbt_is_black(brother->lchild) && rbt_is_black(brother->rchild)) { rbt_set_red(brother); node = parent; } else { /* Case 3: 兄弟結點為黑色(默認), 兄弟節點的右子結點為紅色, 左子結點為黑色: 以brother為支點, 左旋處理 */ if(rbt_is_black(brother->lchild)) { rbt_set_red(brother); rbt_set_black(brother->rchild); rbt_left_rotate(tree, brother); /* 參照結點node不變 */ brother = parent->lchild; } /* Case 4: 兄弟結點為黑色(默認), 兄弟結點左孩子結點為紅色: 以parent為支點, 右旋處理 */ rbt_copy_color(brother, parent); rbt_set_black(brother->lchild); rbt_set_black(parent); rbt_right_rotate(tree, parent); node = tree->root; } } } rbt_set_black(node); return RBT_SUCCESS; }
代碼3 刪除調整
處理結果:
首先,隨機生成左圖樹,再隨機的任意個結點後,得到右圖樹,經過分析可以發現:右圖也是一個紅黑樹。經過反復驗證後,可以判斷以上代碼的處理是正確的。
—— 鄒祁峰
2014.01.18 01:21