1. 程式人生 > >一種新的刪除紅黑樹節點的演算法

一種新的刪除紅黑樹節點的演算法

下面維基百科上紅黑樹的5個性質

1.      節點是紅色或黑色

2.      根是黑色

3.      所有葉子都是黑色(葉子是NIL節點)

4.      每個紅色節點必須有兩個黑色的子節點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點。)

5.      從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

根據上面的5個性質,我們可以得出下面的結論:

結論1:在紅黑樹中若x只有一棵非空子樹,則x必為黑色。

證明:假設x為紅色,根據性質4可以推出x有兩個黑色子節點。與x只有一棵非空子樹矛盾。

結論2:在紅黑樹中若x只有一棵非空子樹。則該非空子樹的根必為紅色,且該非空子樹僅且只有一個根節點。

證明:假設y為x的左孩子,節點y的顏色為黑色,且y有子樹。由於y是黑色,x的右子權為空,所以從x到其左子樹各葉子結點的路徑上黑色結點數大於x到其右子樹葉到葉節點的路徑上黑色節點數,違反性質5,所以節點y為紅色。因為y為紅色,如果y的子樹存在,根據性質4可以得出y的兩棵子樹必為黑色。從x到經過y到各葉節點的路徑上的黑色節點數大於到右子樹葉節點路徑上的黑色節點數。

同上所述,當y為x的右孩子時也可以證明結論2

下面討論紅黑樹的刪除問題:

1. 從上面的兩個結論可以看出,如果要刪除的節點只有一個孩子,那麼直接用孩子節點的值代替父節點的值。刪除子節點就可以,不需要進入刪除調整演算法。

2. 若當前要刪除的節點兩個孩子都不為空,此時我們只需要找到當前節點中序序列的後繼節點。用後繼節點的值替換當前節點的值。將後繼節點作為新的當前節點,此時的當前節點一定只有一個右孩子或左右孩子都為空。

3. 通過步驟2後如果當前節點有後繼節點,直接用其後繼節點值替換當前節點值,不需要進入刪除調整演算法。如果當前節點沒有後繼節點,進入刪除調整演算法。

演算法流程步驟:

1.      判斷x的左右子樹是否為空。

2.      如果x的左右子都不為空,找到中序序列中x的後繼節點y,將x的值用y代替;將y作為新的x節點轉到步驟1。否則轉步驟3

3.      如果x的左子不空,將x的值用左子的值代替,刪除x的左子,演算法結束。否則轉4

如果x的右子不空,將x的值用右子的值代替。

4.      如果x的右子不空,將x的值用右子的值代替,刪除x的右子,演算法結束。否則轉5

5.      刪除x,進入到紅黑樹刪除節點調整程式。

下面通過圖來詳解紅黑樹的刪除過程

首先利用上篇文章中的程式碼生成一棵紅黑樹,插入的順序為:14,9,41,39,47,20,15,22,7,3,28,24

                                                                           圖1:紅黑樹

1.刪除節點22,由於節點22只有一個孩子24,所以直接把22替換成24。根據上面的結論,不需要進行刪除調整演算法。

                                                                  圖2:刪除節點22

2.刪除節點24,24的節點顏色為黑色,且其兄弟39節點也為黑色,39的兩個孩子為空,也為黑,所以將39結點染紅,父節點28作為新的當前節點。由於28是紅色,所以將28染黑,演算法結束。

                                                       圖3:刪除節點24後的紅黑樹

3.刪除節點39,因為39為葉節點,顏色為紅。直接刪除,不需要作任何調整。

     

                                                                   圖4:刪除節點39後的紅黑樹

4.刪除節點28,由於節點28是黑色,其兄弟節點47為黑,兄弟節點的兩孩子也為黑。所以節點47染紅。父節點41變當前節點


                                                     圖5:刪除節點28第一次調整

現在41為當前節點,由於41的兄弟節點14為黑,14的兩個孩子也為黑。所以將14節點染紅。父節點20也就是根節點為當前節點

                                                    圖6:刪除節點28第二次調整

此時20為當前節點,由於20是根節點調整演算法結束,將28從紅黑樹中刪除。

                                                             圖7:刪除節點28後的紅黑樹

至此紅黑樹的屬性全部恢復

下面給出刪除紅黑樹節點及調整的c++程式碼。關於紅黑樹刪除調整演算法可以看July的部落格,他說的比較詳細。這個程式碼是在上一篇 stl map底層之紅黑樹插入步驟詳解與程式碼實現基礎上增加的新功能

刪除節點的c++程式。

template<typename T>
void RBTree<T>::DeleteValue(const T &v1)
{
	RBNode<T>		*p = NULL;
	RBNode<T>		*nextNode = NULL;
	Search(v1,p);
	if(p==NULL)
	{
		cout<<"刪除的值不存在"<<endl;
		return;
	}
	if(p->left && p->right)
	{
		RBNode<T> *tempNode = p->right;
		while(tempNode)
		{//中序序列的後繼節點
			nextNode = tempNode;
			tempNode = tempNode->left;
		}
		p->val = nextNode->val;
		p = nextNode;
	}
	if(p->left)
	{
		//直接用後繼節點的值替換
		RBNode<T> *temp = p->left;
		p->val = temp->val;
		p->left = NULL;
		delete temp;
	}
	else if(p->right)
	{
		//直接用後繼節點的值替換
		RBNode<T> *temp = p->right;
		p->val = temp->val;
		p->right = NULL;
		delete temp;
	}
	else
	{
		//左右子樹都不存在,需要進入刪除調整演算法
		DeleteReblance(p);
		if(p==root)
		{
			root = NULL;
		}
		else if(p==p->parent->left)
		{//父節點的指標域需要修改
			p->parent->left = NULL;
		}
		else if(p==p->parent->right)
		{//父節點的指標域需要修改
			p->parent->right = NULL;
		}
		delete p;
	}
}

紅黑樹刪除後調整演算法:
template<typename T>
void RBTree<T>::DeleteReblance(RBNode<T> *node)
{
	RBNode<T> *parent = NULL;
	RBNode<T> *other = NULL;
	while(node->color==_rb_black_node && node->parent)
	{
		parent = node->parent;
		if(node == parent->left)
		{
			other = parent->right;
			if(other->color==_rb_red_node)
			{//情形1兄弟節點為紅
				parent->color = _rb_red_node;
				other->color = _rb_black_node;
				_rbtree_rotate_left(parent);
				other = parent->right;
			}
			if( (other->left==NULL || other->left->color==_rb_black_node)
				&& (other->right==NULL || other->left->color==_rb_black_node))
			{//情形2兄弟為黑,且兄弟的兩個孩子也為黑
				other->color=_rb_red_node;
				node = parent;
				continue;
			}
			if( other->right==NULL || other->right->color==_rb_black_node)
			{//情形3兄弟節點的右孩子為黑,左為紅
				other->left->color=_rb_black_node;//此時左孩子一定存在且顏色為紅,如果不滿足就不會進入這個條件
				other->color = _rb_red_node;
				_rbtree_rotate_right(other);
				other = parent->right;
			}
			//情形4兄弟節點的右孩子為紅
			other->right->color=_rb_black_node;
			other->color = parent->color;
			parent->color = _rb_black_node;
			_rbtree_rotate_left(parent);
			break;
		}
		else
		{
			other = parent->left;
			if(other->color==_rb_red_node)
			{//情形1兄弟節點為紅
				parent->color = _rb_red_node;
				other->color = _rb_black_node;
				_rbtree_rotate_right(parent);
				other = parent->left;
			}
			if( (other->left==NULL || other->left->color==_rb_black_node)
				&& (other->right==NULL || other->left->color==_rb_black_node))
			{//情形2兄弟為黑,且兄弟的兩個孩子也為黑
				other->color=_rb_red_node;
				node = parent;
				continue;
			}
			if( other->left==NULL || other->left->color==_rb_black_node)
			{//情形3兄弟節點的右孩子為黑,左為紅
				other->right->color=_rb_black_node;//此時左孩子一定存在且顏色為紅,如果不滿足就不會進入這個條件
				other->color = _rb_red_node;
				_rbtree_rotate_left(other);
				other = parent->left;
			}
			//情形4兄弟節點的右孩子為紅
			other->left->color=_rb_black_node;
			other->color = parent->color;
			parent->color = _rb_black_node;
			_rbtree_rotate_right(parent);
			break;
		}
	}
	node->color = _rb_black_node;
}