红黑树及实现(c语言)

本人看的是《算法》这本书,所以红黑树的理解是一下这样的:

个人认为这样比结点标红黑更好理解。很好下面是红黑树的定义:

1、红黑树其实是2-3结点树的二叉平衡表示形式。

2、红链接均为左连接(左倾红黑树)

3、没有任何结点同时连接着两条红链

4、红黑树是完美平衡的,也就是任何空连接(NULL)到根节点经过的黑色分支数量是相等的

其中2、3都是为了维持是2-3树的二叉平衡形式,4、是为了对树高进行一个很好的限制,因为我们知道二叉搜索树的时间复杂度是由树高决定的,所以如果一棵二叉树越平衡那么搜索的性能就约好。

为什么要引进红黑树?直接2-3树不是更好?

这里我们要知道一个问题,2-3树的插入和删除的分类情况是非常多的,而且插入和删除后为了维持树的绝对平衡性,会带来许多的操作,这样就会对树的运行效率带来极大的挑战,也许此时额外的负担已经超过维持平衡后所带来的收益了。而我们反观红黑树,单从树的角度来看并不是完美平衡的,但是我们有条件4的限制,那么会出现一个有趣的效果,那就是最长的结点高度不会高于任意结点高度的两倍,并且红黑树的插入和删除情况少,维护的代价低。那么我们可以看出下面一个收益:

TotalCost = BalanceCost + SearchCost + InsertCost + DeleteCost。所以我们可以把红黑树看成是一种2-3树的变体(在插入,删除过多的情况下),也就是在某些情况下TotalCost会更加小的特殊情况。

下面是对红黑树的实现:

数据结构:

#define Red true;
#define Black false;

typedef struct treeNode{
	KeyType Key;
	ValType val;
	struct treeNode *left;
	struct treeNode *right;
	bool color;
}RBTree,*PRBTree;

1、插入:

//插入函数
PRBTree insertKey(PRBTree root,char key,int val)
{
	//空就生成新结点
	if(!root)
	{
		CreateNode(&root,key,val);
		return root;
	}
	if(root->Key > key)
		root->left =  insertKey(root->left,key,val);
	else if(root->Key < key)
		root->right =  insertKey(root->right,key,val);
	else
		root->val = val;
	if(isRed(root->left) && isRed(root->right))
		colorFlip(root);
	if(isRed(root->left) && isRed(root->left->left))
	{
		root = rightRotate(root);
		colorFlip(root);
	}
	if(isRed(root->right))
		root = leftRotate(root);
	return root;
}

2、删除函数

//删除任意结点
void deleteNode(PRBTree *root,char key)
{
	//----------------------------------策略是利用删除最大和最小函数-------------------------------------
	PRBTree T;
	if(!(*root))
		return;
	//在左子树
	if((*root)->Key > key)
	{
		if(isRed((*root)->left) || isRed((*root)->left->left));
		else if(!isRed((*root)->left) && !isRed((*root)->right))
			borrowKeyMin(root);
		deleteNode(&(*root)->left,key);
	}
	else if((*root)->Key < key)
	{
		if(isRed((*root)->left))
			*root = rightRotate(*root);
		else if((*root)->right)
		{
			if(isRed((*root)->right->left))
				(*root)->right = rightRotate((*root)->right);
			else if(!isRed((*root)->left) && !isRed((*root)->right))
				borrowKeyMax(root);
		}
		deleteNode(&(*root)->right,key);
	}
	else
	{
		//存在左子树,那么用左子树的最大值来代替并且删除
		if((*root)->left)
		{
			PRBTree T = DeleteMax(&(*root)->left);
			(*root)->Key = T->Key;
			(*root)->val = T->val;
		}
		//存在右子树,用右子树的最小值代替并且删除
		else if((*root)->right)
		{
			T = DeleteMin(&(*root)->right);
			(*root)->Key = T->Key;
			(*root)->val = T->val;
		}
		//叶子结点,因为前面每一步都是经过变换的,所以可以直接删除
		else
			(*root) = NULL;
	}
	*root = fixUp(*root);
}

3、插入删除的辅助函数

//判断结点的颜色
bool isRed(PRBTree Node);

//颜色反转函数
void colorFlip(PRBTree Node);

//插入函数
PRBTree insertKey(PRBTree root,char key,int val);

//插入函数,需要刷新一下根节点
PRBTree insert(PRBTree root,char key,int val);

//生成一个新的结点
void CreateNode(char key,int val);

//右旋转
PRBTree rightRotate(PRBTree Node);

//左旋转
PRBTree leftRotate(PRBTree Node);

//展示红黑树
void Display(PRBTree root);

//利用插入函数直接创建一个红黑树
PRBTree CreateRBTree(char *key,int *val,int keySize);

//删除最小键
PRBTree deleteMin(PRBTree *root);

//删除最小键的直接调用函数,需要重置根
PRBTree DeleteMin(PRBTree *root);

//删除最大键
PRBTree deleteMax(PRBTree *root);

//删除最大键的直接调用函数,需要重置根
PRBTree DeleteMax(PRBTree *root);

//借键的情况Max
void borrowKeyMax(PRBTree *Node);

//借键的情况Min
void borrowKeyMin(PRBTree *Node);

//修复红黑树
PRBTree fixUp(PRBTree root);

//删除任意结点
void deleteNode(PRBTree *root,char key);

//删除任意结点,直接调用函数
PRBTree DeleteNode(PRBTree *root,char key);

//查找函数
bool SearchKey(PRBTree root,char key,PRBTree *find);

4、关键的辅助函数的实现:

//颜色反转函数
void colorFlip(PRBTree Node)
{
	if(isRed(Node))
	{
		Node->color = Black;
	}
	else
		Node->color = Red;
	if(Node->left)
	{
		if(isRed(Node->left))
		{
			Node->left->color = Black;
		}
		else
			Node->left->color = Red;
	}
	if(Node->right)
	{
		if(isRed(Node->right))
		{
			Node->right->color = Black;
		}
		else
			Node->right->color = Red;
	}
}

//右旋转
PRBTree rightRotate(PRBTree Node)
{
	PRBTree Temp = Node->left;
	Node->left = Temp->right;
	Temp->right = Node;
	Temp->color = Node->color;
	Node->color = Red;
	return Temp;
}

//左旋转
PRBTree leftRotate(PRBTree Node)
{
	PRBTree Temp = Node->right;
	Node->right = Temp->left;
	Temp->left = Node;
	Temp->color = Node->color;
	Node->color = Red;
	return Temp;
}
//修复红黑树
PRBTree fixUp(PRBTree root)
{
	if(!root)
		return NULL;
	if(isRed(root->right) && !isRed(root->left))
		root = leftRotate(root);
	if(isRed(root->left) && isRed(root->left->left))
		root = rightRotate(root);
	if(isRed(root->left) && isRed(root->right))
		colorFlip(root);
	return root;
}

//借键的情况
void borrowKeyMin(PRBTree *Node)
{
	colorFlip(*Node);
	if((*Node)->right)
	{
		if(isRed((*Node)->right->left))
		{
			(*Node)->right = rightRotate((*Node)->right);
			(*Node) = leftRotate((*Node));
			colorFlip((*Node));
		}
	}
}

//借键的情况Max
void borrowKeyMax(PRBTree *Node)
{
	colorFlip(*Node);
	if((*Node)->left)
	{
		if(isRed((*Node)->left->left))
		{
			(*Node) = rightRotate((*Node));
			colorFlip((*Node));
		}
	}
}

//删除最小键
PRBTree deleteMin(PRBTree *root)
{
	//-------------------------------目的是确保需要删除的结点的上一个节点不是2结点,这样就可以直接删除--------------------------------
	//可以删除的结点
	if(!(*root))
		return NULL;
	if(isRed(*root) && !(*root)->left)
	{
		PRBTree Temp = *root;
		*root = (*root)->right;
		return Temp;
	}
	//只剩最后一个结点的情况
	if(!(*root)->left && !(*root)->right)
	{
		PRBTree Temp = *root;
		*root = NULL;
		return Temp;
	}
	//需要借键的情况
	if(!isRed((*root)->left) && !isRed((*root)->right))
		borrowKeyMin(root);
	PRBTree Temp = deleteMin(&(*root)->left);
	(*root) = fixUp(*root);
	return Temp;
}

//删除最大键
PRBTree deleteMax(PRBTree *root)
{
	//-------------------------------目的是确保需要删除的结点的上一个节点不是2结点,这样就可以直接删除--------------------------------
	//将原本就是3结点的结点旋转成符合删除最大的形式
	if(!(*root))
		return NULL;
	if(isRed((*root)->left))
		*root = rightRotate(*root);
	else if((*root)->right)
	{
		if(isRed((*root)->right->left))
			(*root)->right = rightRotate((*root)->right);
	}
	//可以删除的结点
	if(isRed((*root)) && !(*root)->right)
	{
		PRBTree Temp = (*root);
		(*root) = (*root)->left;
		return Temp;
	}
	//只剩最后一个结点的情况
	if(!(*root)->left && !(*root)->right)
	{
		PRBTree Temp = *root;
		*root = NULL;
		return Temp;
	}
	//需要借键的情况
	if(!isRed((*root)->left) && !isRed((*root)->right))
		borrowKeyMax(root);
	PRBTree Temp = deleteMax(&(*root)->right);
	(*root) = fixUp(*root);
	return Temp;
}

想看思路的可以看这里,很清楚:

https://www.jianshu.com/p/37c845a5add6

猜你喜欢

转载自blog.csdn.net/c_living/article/details/81136608