1. 程式人生 > >紅黑樹 節點的刪除

紅黑樹 節點的刪除

接上一篇。

紅黑樹的刪除繼續分各種情況進行考慮。

首先考慮紅黑樹的單支情況,即只有父節點只有一個子節點,另外一個為NULL,這樣的話,只有一種情況,即父節點為黑色,子節點為紅色,因為其他情況都會是孩子為黑色,因為孩子為紅色父為紅色,則與紅父黑子矛盾。而當孩子是黑色時,另一個孩子是黑色的NULL,則必定使左右路徑上黑色節點數量不等。所以單支只能為黑父紅子。

這裡的單支情況,是對任意一點的,若其只有一個孩子,則其必為黑色,孩子必為紅色,且孩子為葉子節點。孩子為紅色,如果有孫子,其孫子必為黑色,則路徑黑子數量不等,不平衡。

情況一:

單支,刪除紅色節點,則不會影響其他路徑情況,不影響平衡,直接刪除即可。

情況二:

單支,刪除黑節點

若s節點為黑色,則用單支下的紅色孩子替代自己,然後刪除黑色節點,這樣路徑上少了一個黑色節點,影響平衡,需要進行調整。

情況三:

刪除節點有兩個非空孩子,這時候又用到向下旋轉的思路,尋找刪除節點p的中序遍歷的後繼節點s,即正常排序後位於p之後的數字,而且s節點必定最多有一顆子樹,即s最多有一顆右子樹,或者沒有。若s節點有一顆右子樹,則必為單支情況,其孩子為紅色,且無孫子,則這就是情況二,轉到情況二。若s節點為葉子節點,則其可為紅色或黑色,紅色為情況一。

這種情況將p與s的資料交換,但顏色不換,則情況轉換為刪除s點,而刪除之前樹還是平衡的。

或者這樣分析:

若s節點為紅色,則其必定不是單支,則其為葉子節點,則是情況一,直接刪除即可。

若s節點為黑色,則用s的孩子null或者單支下的紅色孩子替代自己,然後刪除黑色節點,這樣路徑上少了一個黑色節點,影響平衡,需要進行調整。(這裡又要注意,如果是孩子為NULL,替代後,如何找到s節點的父節點。沒有哨兵節點,這個地方麻煩了。。。。好吧,呼叫的遞迴調整函式的引數加一個父節點指標)

然後如同插入一樣設定一個遞迴的調整函式,調整有4種情況:

首先以刪除判斷節點為x,為黑色,父節點為p,兄弟節點為w。以x為根的子樹的黑色節點樹減一,則需要進行平衡操作。

一般是有一下4種情況,但,還有一個特殊情況,即x為根節點時,跟插入時判斷相同,如果x為根節點,且現在為紅色, 則將其改為黑色。對於以下情況2,可能轉換到這種情況。第二次被別人的部落格坑,等會寫完了再測試有沒有其他錯誤。

情況1:w為紅色。

則w的孩子都為黑色,p為黑色。改變w和p的顏色,即w改為黑色,p改為紅色,然後以p為支點進行左旋,或右旋,根據x位於p的位置決定,若x為左孩子,左旋,右孩子右旋。更新後x獲得了新的兄弟節點,然後繼續對x進行判斷調節。

情況2:w為黑色,且w的孩子都為黑色。這裡由於之前是在x所在子樹上刪除了一個黑色的節點,則w所在子樹上必定有一個黑色節點,即w必定不為NULL。

將w置為紅色,使w所在子樹上黑色數量減一,則p所在子樹上黑色節點數量也減一,則需要以p節點作為當前節點,即新的x節點。如果新的x為紅,則將其置為黑色,則整個樹平衡,結束,如果為黑,則繼續判斷新的x節點的情況。

情況3:w為黑色。

如果x為p的左孩子, w的左孩子為紅色,右孩子為黑色。

則將w置為紅色,左孩子為黑色,然後以w節點為支點右旋。

如果x為p的右孩子, w的右孩子為紅色,左孩子為黑色、

則將w為紅,右孩子為黑,然後以w節點左旋。

獲得新的兄弟節點,進入情況4. 這裡進行情況3的旋轉不會對改變任何子樹的平衡性。

情況4:w為黑色。

如果x為p的左孩子,w的右孩子為紅色,左孩子可以為紅色或黑色或NULL。

則交換w和p的顏色,因為之後w要成為新的父節點,所以要獲得原來p的顏色,防止與p的父節點衝突。

將w的右孩子設定為黑色,這樣w的右子樹黑色節點樹+1,

再以p節點為支點左旋,這樣w的顏色黑色會移動到左子樹,使其節點樹 -1 + 1 =0,而右子樹減少了一個黑節點 +1 -1 = 0;且新的p節點為原來的顏色,整個樹是平衡的。結束、

如果x為p的右孩子,w的左孩子是紅色。同理、

交換w和p的顏色,將w的做孩子改為黑色,以p為節點右旋。結束判斷。

最終調整完成。這裡也有一個很重要的地方,在左旋和右旋時交換父節點p與其對應左右孩子c的顏色,導致旋轉後得到的新父節點p顏色未變,則不用再向上檢測。

中間測試的時候有出錯了,嘗試畫了一些圖並找出了錯誤,只畫了一張圖,其他都是看別人的部落格思考的。


刪除其實也不是特別複雜,嘗試寫出程式碼:

template <typename T>
void RBTree<T>::GetParentLinked(RBNode<T> *a,RBNode<T> *b){
	if(a == root){
		root = b;
		if(b)
			b->parent = 0;
		return;
	}
	if(a->parent->left == a){
		a->parent->left = b;
		if(b)
			b->parent = a->parent;
	}else{
		a->parent->right = b;
		if(b)
			b->parent = a->parent;
	}

}
template <typename T>
bool RBTree<T>::Delete(const T &x){
	RBNode<T> *p = root ,*s,*fa;
	if(!root)return false;
	while(p){
		if(p->value == x)break;
		else if(x < p->value)p = p->left;
		else p = p->right;
	}
	if(p){
		if( p->left &&  p->right){//當存在左右孩子時,向下旋轉
			s = p->right;
			while(s->left){
				s = s->left;
			}
			int temp = p->value;
			p->value = s->value;
			s->value = temp;
			p = s;
		}
		if(p->red){//為紅色葉子節點,直接刪除
			GetParentLinked(p,p->left);
			delete p;
		}else{//為黑色時,將其紅色子孩子替代他
			//由於之前已經向下旋轉,則這裡只有一種特殊可能,即單支情況刪除黑色根節點。			
			fa = p->parent;//之後可能沒有父節點。
			if(p->left){
				s = p->left;
				GetParentLinked(p,p->left);				
			}
			else {
				s = p->right;
				GetParentLinked(p,p->right);
			}
			delete p;
			AdjustAfterDelete(s,fa);
		}
		return true;
	}else return false;

	
}
template <typename T>
void RBTree<T>::AdjustAfterDelete(RBNode<T> *x ,RBNode<T> *p){
	if(p){//如果有父指標,則有四種情況。
		RBNode<T> *w ;
		bool inLeftTree = true;//標記左右情況,左右情況不同旋轉方向以及判斷方式也不同。
		if(p->left == x)w = p->right;
		else {
			w = p->left;
			inLeftTree = false;
		}
		if(w->red){
			w->red = false;
			p->red = true;
			if(inLeftTree)
				LRotation(p);//以p為支點進行旋轉,不用考慮當前節點是否為空。
			else
				RRotation(p);
			AdjustAfterDelete(x,p);
		}else{
			if( !GetColorWithNULL(w->left) && !GetColorWithNULL(w->right) ){
				w->red = true;
				x = p;
				if(x ->red)x->red = false;//出錯一次,要根據新的x節點進行判斷。
				else	AdjustAfterDelete(x,x->parent);
			}else if(( inLeftTree && GetColorWithNULL(w->right) ) //情況4
				|| ( !inLeftTree && GetColorWithNULL(w->left) )  ){
					bool tempRed = p->red;
					p->red = w->red;
					w->red = tempRed;
					if(inLeftTree){
						w->right->red = false;						
						LRotation(p);
					}else{
						w->left->red = false;
						RRotation(p);
					}
			}else {//情況3
				w->red = true;
				if(inLeftTree){
					w->left->red = false;
					RRotation(w);
					AdjustAfterDelete(x,p);
				}else{
					w->right->red = false;
					LRotation(w);
					AdjustAfterDelete(x,p);
				}
			}
		}
	}else{//如果不存在父節點,則此節點為根節點,則置為黑色結束。
		if(x)
			x->red = false;
		return ;
	}

}