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

原创 2012年03月24日 22:38:09

参照算法导论简单实现了一下红黑树的节点插入,对于多次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));
			}
		}
	}
}




相关文章推荐

红黑树插入删除节点过程分析 && C代码实现

红黑树的插入和删除规则: 红黑树的五个性质 1、    每个节点要么是红的,要么是黑的 2、    根节点时黑色的 3、    每个叶节点(叶节点既指树尾端NIL指针或NULL节点)是黑色的 ...

算法导论 红黑树 节点删除

RB-DELETE-FIXUP:需要执行Fixup的大前提是:被删除节点y是黑节点。 先说一下删除节点时可能违背的特点。特点一:节点或红或黑。不违背。特点2:根节点是黑节点。当被删除的节点是黑节点,而...

算法导论 红黑树加入节点

RB-INSERT-FIXUP:加入一个新节点时可能出现的问题:1.被加入节点是红节点,且原红黑树为空,这时只要把节点x涂成黑色即可。2.被加入的红节点的父节点也是红节点,即z和p[z]都为红色,这就...

红黑树之一(基本性质,插入节点)

平衡二叉树(AVL)是一种具有很好的性能的排序二叉树,但是也并不完美。如果所需要维护数据变化也比较频繁,这就需要经常对ALV树进行调整,由于平衡二叉树对其子树的限制太严格,因而进行插入或者删除时经常需...

二叉搜索树---红黑树节点插入

1.红黑树的概念:红黑树是一棵二叉搜索树,它在每个节点上增加一个存储位来表示节点的颜色,可以是red或black,通过对任何一条从根节点到叶子节点上的间单路径来约束,红黑树保证最长路径不超过最短路径的...

红黑树插入某个节点的方法

红黑树节点的插入分为两步:第一步与二叉查找树的插入相同,第二步为调整节点着色。   节点插入   新节点Z的两个孩子指向NIL,将节点z链入树的正确位置,将节点z着色为红。   着色调整   ...

Linux红黑树(三)——插入节点

性质1. 节点是红色或黑色。 性质2. 根是黑色。 性质3. 所有叶子都是黑色(叶子是NIL节点)。 性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色...

学习之旅-红黑树之插入新节点

关于红黑树的帖子不可谓不多。一开始我看的是july的帖子(链接:http://blog.csdn.net/v_JULY_v/article/details/6105630),但是删除的时候他这系列说的...

Java红黑树代码-节点

  • 2012年02月23日 13:33
  • 3KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:红黑树的节点插入算法实现
举报原因:
原因补充:

(最多只允许输入30个字)