1. 程式人生 > >資料結構-----紅黑樹的插入操作

資料結構-----紅黑樹的插入操作

紅黑樹是一棵二叉搜尋樹;樹種每一個節點的顏色不是黑色就是紅色。本篇中只實現用節點的顏色來描述紅黑樹,性質如下:

RB1:根節點和所有外部節點都是黑色;

RB2:在根至外部節點路徑上,沒有連續兩個節點是紅色;

RB3:在所有根至外部節點的路徑上,黑色節點的數目都相同;

實現紅黑樹,首先需要定義樹節點,節點包括:

1:節點顏色,黑色或者白色,用整型或者布林型別表示,也可以預設兩個巨集;

2:節點權值,用來儲存節點的資料;

3:左右子樹指標;

首先是紅黑樹的定義,本例中紅黑樹繼承自BST(二叉搜尋樹),即:

#define BLACK 1
#define RED 0

template<class T>
class RBTree : public binarySearchTree<T, int>
{
public:
	RBTree() : binarySearchTree() {}
	~RBTree() {}

	void insert(const T& key) {insert(root, key);}
	void remove(const T& key) {remove(root, key);}

private:
	binaryTreeNode<pair<const T, int>>*
		insert(binaryTreeNode<pair<const T, int>>*&, const T&);

	binaryTreeNode<pair<const T, int>>*
		remove(binaryTreeNode<pair<const T, int>>*&, const T&);

	binaryTreeNode<pair<const T, int>>*
		maximun(binaryTreeNode<pair<const T, int>>*);
	binaryTreeNode<pair<const T, int>>*
		minimun(binaryTreeNode<pair<const T, int>>*);

	int color(binaryTreeNode<pair<const T, int>>*);

}

和AVL樹類似,插入刪除操作也是利用遞迴實現,所以在私有成員中存在實現遞迴呼叫的過載函式。

模板引數T為節點權值的型別,int為顏色型別。

insert插入函式:在以該節點為根節點的子樹中插入新權值,返回插入後該子樹的根節點。

remove刪除操作:在以該節點為根節點的子樹中刪除特定權值的節點,返回刪除後子樹的根節點。

maximun函式:返回以該節點為根節點的子樹中權值最大的節點。

minimun函式:返回以該節點為根節點的子樹中權值最小的節點。

color函式:返回函式的顏色。

template<class T>
int RBTree<T>::color(binaryTreeNode<pair<const T,int>>* pnode)
{
	if(pnode == NULL)
		return BLACK;
	return pnode->element.second;
}


插入操作步驟如下:
步驟1:找到待插入的位置,即逐個比較權值,若該節點權值小於待插入權值,則在右子樹中插入;若該節點權值大於待插入權值,則在左子樹中插入。

步驟2:申請新節點,若樹為空,則新節點為黑色;若樹非空,則新節點為紅色。

步驟3:改變相關節點的顏色,調整樹平衡。

申請新節點的程式碼如下:

template<class T>
binaryTreeNode<pair<const T, int>>*
RBTree<T>::insert(binaryTreeNode<pair<const T, int>>* &pnode, const T& key)
{
	if(pnode == NULL)
	{
		if(root == NULL)
			pnode = new binaryTreeNode<pair<const T, int>>*
						(pair<const T, int>(key, BLACK));
		else
			pnode = new binaryTreeNode<pair<const T, int>>*
						(pair<const T, int>(key, RED));	
	}
	/*...*/
}

接下來就是執行比較插入的過程,涉及到對節點顏色進行修改的內容,我們先了解一下需要哪些調整。

插入操作的樹平衡被破壞無非是存在兩個連續的紅色節點,違反了RB2。

同時,若違反RB2,則新插入節點和其父節點都應為紅色,那麼祖父節點必須為黑色(因為插入之前是平衡的)。

被破壞的情況有以下幾種,假設子樹的根節點為pnode,插入的節點為ptNode:

第一種情況:pnode的左孩子和右孩子都為紅色,新節點是pnode孩子的孩子。

插入節點為pnode->leftChild->leftChild或pnode->leftChild->rightChild	
color(pnode->leftChild) == RED && color(pnode->rightChild) == RED  
                              或者					
			
插入節點為pnode->rightChild->leftChild或pnode->rightChild->rightChild	
color(pnode->rightChild) == RED && color(pnode->leftChild) == RED    

解決方案:

1:將根節點的左右子樹的顏色塗上黑色;

2:若pnode不是整個紅黑樹的根節點,則將pnode塗上紅色

3:返回pnode;

程式碼實現:

template<class T>
binaryTreeNode<pair<const T,int>>*
RBTree<T>::rModify(binaryTreeNode<pair<const T, int>>* pnode)
{
	pnode->leftChild->element.second = BLACK;
	pnode->rightChild->element.second = BLACK;
	if(pnode != root)
		pnode->element.second = RED;
	return pnode;
}


第二種情況:pnode的左孩子為紅色,右孩子為黑色(也可以是外部節點),新節點是pnode左孩子的孩子。

插入節點為pnode->leftChild->leftChild或pnode->leftChild->rightChild
color(pnode->leftChild) == RED && color(pnode->rightChild) == BLACK 						
解決方案:

1:若插入的是pnode左孩子的左孩子,則進行右旋操作;若插入的是pnode左孩子的右孩子,則進行先左旋後右旋操作;此時pnode為新根節點。

2:將新的根節點塗黑,將新根節點右孩子塗紅。

3:返回根節點。

程式碼實現:

template<class T>
binaryTreeNode<pair<const T,int>>*
RBTree<T>::LbModify(binaryTreeNode<pair<const T, int>>* pnode)
{
	pnode->element.second = BLACK;
	pnode->rightChild->element.second = RED;
	return pnode;
}
函式呼叫:
if(color(pnode->rightChild) == BLACK && color(pnode->leftChild) == RED)
{
	if(key > pnode->leftChild->element.first)
		pnode = leftRightRotation(pnode);
	else
		pnode = rightRotation(pnode);
	pnode = LbModify(pnode);
}


第三種情況:pnode的右孩子為紅色,左孩子為黑色,新節點為pnode右孩子的孩子。
插入節點為pnode->rightChild->leftChild或pnode->rightChild->rightChild	
color(pnode->rightChild) == RED && color(pnode->leftChild) == BLACK  

解決方案:

1:若新節點是pnode右孩子的右孩子,進行左旋操作;若新節點是pnode右孩子的左孩子,進行先右旋後左旋操作,此時pnode為新的根節點。

2:將新的根節點塗黑,新根節點的左孩子塗紅。

3:返回新根節點。

程式碼實現:

template<class T>
binaryTreeNode<pair<const T,int>>*
RBTree<T>::RbModify(binaryTreeNode<pair<const T, int>>* pnode)
{
	pnode->element.second = BLACK;
	pnode->leftChild->element.second = RED;
	return pnode;
}
呼叫程式碼:
if(color(pnode->leftChild) == BLACK && color(pnode->rightChild) == RED)
{
	if(key > pnode->rightChild->element.first)
		pnode = leftRotation(pnode);
	else
		pnode = rightLeftRotation(pnode);
	pnode = RbModify(pnode);
}

插入函式程式碼:
template<class T>
binaryTreeNode<pair<const T, int>>*
RBTree<T>::insert(binaryTreeNode<pair<const T, int>>* &pnode, const T& key)
{
	if(pnode == NULL)	//申請新節點,空則為黑色,非空則為紅色。
	{
		if(root == NULL)
			pnode = new binaryTreeNode<pair<const T, int>>*
						(pair<const T, int>(key, BLACK));
		else
			pnode = new binaryTreeNode<pair<const T, int>>*
						(pair<const T, int>(key, RED));	
	}
	else
	{
		if(key < pnode->element.first)
		{
			//如果key小,則在左子樹中插入
			pnode->leftChild = insert(pnode->leftChild, key);
			//左子樹中插入後,若左孩子的孩子是紅色,則需要進一步判斷。
			if(pnode->leftChild->leftChild != NULL && color(pnode->leftChild->leftChild) == RED)||
			   (pnode->leftChild->rightChild != NULL && color(pnode->leftChild->rightChild) == RED)
			{
				//若左孩子是紅色,則需要調整
				if(color(pnode->leftChild) == RED)
				{
					if(color(pnode->rightChild) == RED)	//若右孩子是黑色,為第一種情況
						pnode = rModify(pnode);
					else				//否則,為第二種或者第三種
					{
						if(key > pnode->leftChild->element.first)
							pnode = leftRightRotation(pnode);
						else
							pnode = rightRotation(pnode);
						pnode = LbModify(pnode);
					}
				}
			}
		}
		//與上述對稱
		else if(key > pnode->element.first)
		{
			pnode->rightChild = insert(pnode->rightChild, key);
			if(pnode->rightChild->leftChild != NULL && color(pnode->rightChild->leftChild) == RED) ||
				(pnode->rightChild->rightChild != NULL && color(pnode->rightChild->rightChild) == RED)
			{
				if(color(pnode->rightChild) == RED)
				{
					if(color(pnode->leftChild) == RED)
						pnode = rModify(pnode);
					else
					{
						if(key > pnode->rightChild->element.first)
							pnode = leftRotation(pnode);
						else
							pnode = rightLeftRotation(pnode);
						pnode = RbModify(pnode);
					}
				}
			}
		}
	}

	return pnode;
}