关闭

红黑树的节点插入算法实现

标签: 算法nulltreeinsertdeletesearch
2606人阅读 评论(0) 收藏 举报
分类:

参照算法导论简单实现了一下红黑树的节点插入,对于多次static_cast的使用,我表示面向对象那块学的不好,谁有好的方法可以告诉我一下。


首先实现一棵二叉查找树,类的声明如下:

typedef int treeKeyType;


class BinSearchTreeNode
{
public:
	BinSearchTreeNode(treeKeyType k):key(k)
	{
		left=NULL;
		right=NULL;
		parent=NULL;
	}
	BinSearchTreeNode * left;
	BinSearchTreeNode * right;
	BinSearchTreeNode * parent;
	treeKeyType key;
};


class BinSearchTree
{
public:
	BinSearchTreeNode * root;
	BinSearchTree():root(NULL){}
	~BinSearchTree(){}
		// 中序遍历
	void inOrderPrint(BinSearchTreeNode *);
		// 前序遍历
	void preOrderPrint(BinSearchTreeNode *);
		// 后序遍历
	void proOrderPrint(BinSearchTreeNode *);
		// 中序遍历
	void inOrder();
		// 前序遍历
	void preOrder();
		// 后序遍历
	void proOrder();
		// find the node who is equal to key
	BinSearchTreeNode * treeSearch(treeKeyType key);
		// return the minimum value of tree which has root x
	BinSearchTreeNode * minNode(BinSearchTreeNode * x);
		// return the maximum value of tree which has root x
	BinSearchTreeNode * maxNode(BinSearchTreeNode * x);
		// return the successor of node x
	BinSearchTreeNode * successor(BinSearchTreeNode * x);
		// return the predessor of node x
	BinSearchTreeNode * predessor(BinSearchTreeNode * x);
		// insert node z into the tree
	void insertNode(BinSearchTreeNode * newNode);
		// delete node z from the tree
	void deleteNode(BinSearchTreeNode * z);
};


因为红黑树也有孩子节点,父亲节点,关键词等成员,这点与二叉查找树的节点非常类似,所以我们红黑树的节点直接继承自二叉查找树的节点,只不过多了一个颜色成员color。

但是红黑树相比二叉查找树来说,必须满足以下五条性质:

(1)每个节点或者红,或者黑;

(2)根节点必须是黑的;

(3)每个叶子节点(或者说NIL,空节点)是黑的;

(4)每一个红的节点的父亲节点必须是黑的;

(5)对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点,我们将这个数目成为黑高度。

首先,介绍一下红黑树的左旋(left-rotate)和右旋(right-rotate)操作,这两个操作是互逆的。

#define RED 0
#define BLACK 1
typedef int Color;

class RedBlackTreeNode : public BinSearchTreeNode
{
public:
	Color color;
	RedBlackTreeNode(treeKeyType key, Color c_color=BLACK):BinSearchTreeNode(key), color(c_color)
	{
	}
};

首先,介绍一下红黑树的左旋(left-rotate)和右旋(right-rotate)操作,这两个操作是互逆的。

左旋操作的过程如下:

(1)首先将x设置为y的左孩子;

(2)y的左孩子Beta的父亲设置为为x;

(3)将y的父亲节点设置为x的父亲节点;

(4)判断一下x以前是否作为根节点,如果是,将当前树的跟改成y;接下来再判断x以前作为左孩子还是右孩子,如果是左孩子,将y设置为x的爷爷的左孩子,否则设置为x的爷爷的右孩子;

(5)x成为y的左孩子,y成为x的父亲。

右旋,反之。。。



左旋和右旋的实现代码如下:

// 左旋操作
void RedBlackTree::leftRotate(RedBlackTreeNode * x)
{
	RedBlackTreeNode * y=static_cast<RedBlackTreeNode *>(x->right);
	x->right=y->left;
	if(y->left != NULL)
		y->left->parent=x;
	y->parent=x->parent;
	if(x->parent == NULL)	//then x is current root
		root=y;
	else if(x->parent->right == x)
		x->parent->right=y;
	else
		x->parent->left=y;
	y->left=x;
	x->parent=y;
}

// 右旋操作
void RedBlackTree::rightRotate(RedBlackTreeNode * y)
{
	RedBlackTreeNode * x=static_cast<RedBlackTreeNode *>(y->left);
	y->left=x->right;
	if(x->right != NULL)
		x->right->parent=y;
	if(y->parent == NULL)	//then y is currrent root 
		root=x;
	else if(y == y->parent->left)
		y->parent->left=x;
	else
		y->parent->right=x;
	x->right=y;
	y->parent=x;
}

红黑树的插入,实现如下:

在调用修复函数之前的插入操作,都和二叉查找树的插入操作类似,用两个指针x和y分别代表父子节点,一级级的往下找,直到x为空,y作为插入位置(即插入节点,作为y的孩子),判断y的关键词的大小和要插入节点关键词的大小来确定是作为y的左孩子还是右孩子。同时记住将新插入的节点z的颜色置为红色,z的两个孩子节点作为黑色的NIL节点(就是NULL),这样保证不改变树的黑高度。

// 插入节点
void RedBlackTree::insertNode(RedBlackTreeNode * z)
{
	// first, do the insertion like Binary Search Tree
	RedBlackTreeNode * x=static_cast<RedBlackTreeNode *>(root), * y=NULL;
	while(x != NULL)
	{
		y=x;
		if(z->key < x->key)
			x=static_cast<RedBlackTreeNode *>(y->left);
		else
			x=static_cast<RedBlackTreeNode *>(y->right);
	}
	z->parent=y;
	if(y == NULL)	//说明当前树为空
		root=z;
	else if(z->key < y->key)
		y->left=z;
	else
		y->right=z;
	z->left=NULL;
	z->right=NULL;
	z->color=RED;

	//调用红黑树修复函数
	insertFixUp(z);
}

红黑树的修复操作是为了保持红黑树的性质,实现如下:

这里分三种情况:case1,case2和case3,如图所示。


case 1

图中z是当前要插入的节点,只有当z的父亲为红色时,我们才有必要执行修复操作;因为如果z的父亲为黑色的话,红黑树性质(4):所有红色节点的父亲必须为黑色也会满足,从而所有红黑树的性质都满足了,直接退出算法就可以。无论何种情况,新插入的节点都要置为红色以保证黑高度不变。

case1代表的情况是z的叔叔(叔叔就是爷爷的相对于爸爸的另一个孩子)也是红色的。这时将z的父亲、叔叔和爷爷的颜色取反。让z指向新的位置,即z的爷爷。继续循环。


case 2 and case 3

如果z的叔叔是黑色的,那就进入case2或者case3的情况,可能是先执行case2后执行case3,也可能只执行case3,无论如何,case3都是要执行的。

case2代表的情况是当前要插入的节点z是其父亲A的右孩子,更一般的情况,应该说z和其父亲之间的连线l1和z的父亲和z的爷爷之间的连接线l2成一个锐角。(估计大家应该能明白是什么意思,就是B是A的右孩子,A是C的左孩子。但是我们在考虑case1,case2,case3时,仅仅考虑了一半的情况:即z的父亲A是z的爷爷z的左孩子,另一半情况当然是A是C的右孩子了!那些情况也会分case1,case2,case3,跟现在讨论的完全类似。)对于case2,对z的父亲做一次左旋操作,就会变成case3的情况。然后在z的爷爷位置做一次右旋操作,同时将z的父亲和z的爷爷的颜色取反。此时,z的父亲变为黑色了,(我们认为z的父节点如果变为黑色后就满足红黑树性质了),循环自动退出。

总的来说,case1有可能执行多次,但是case2和case3只有可能执行一次。

// 需要进行修复,保持红黑树的黑高度一致等性质不变
void RedBlackTree::insertFixUp(RedBlackTreeNode * z)
{
	while( z->parent != NULL && static_cast<RedBlackTreeNode *>(z->parent)->color == RED )
	{
		if(z->parent->parent->left == z->parent)	
		{
			RedBlackTreeNode * ancle =static_cast<RedBlackTreeNode *>(z->parent->parent->right);
			if(ancle->color == RED)	// case1
			{
				static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;
				static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;
				ancle->color=BLACK;
				z=static_cast<RedBlackTreeNode *>(z->parent->parent);
			}
			else 
			{
				if(z->parent->right == z)			// case2
				{
					z=static_cast<RedBlackTreeNode *>(z->parent);
					leftRotate(z);
				}
				// case3
				static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;
				static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;
				rightRotate(static_cast<RedBlackTreeNode *>(z->parent->parent));
			}
		}
		else // 当前节点的父亲是当前节点爷爷的右孩子
		{
			RedBlackTreeNode * ancle=static_cast<RedBlackTreeNode *>(z->parent->parent->left);
			if(ancle->color == RED)	// case1
			{
				static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;
				static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;
				ancle->color=BLACK;
				z=static_cast<RedBlackTreeNode *>(z->parent->parent);
			}
			else
			{
				if( z == z->parent->left )		// case2
				{
					z=static_cast<RedBlackTreeNode *>(z->parent);
					rightRotate(z);
				}
				// case3
				static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;
				static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;
				leftRotate(static_cast<RedBlackTreeNode *>(z->parent->parent));
			}
		}
	}
}




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:50918次
    • 积分:643
    • 等级:
    • 排名:千里之外
    • 原创:12篇
    • 转载:2篇
    • 译文:0篇
    • 评论:17条
    文章分类
    最新评论