红黑树的插入操作

红黑树插入时的情况
红黑树是有序树,在插入一个节点时,插入的结点一定会成为叶结点,且插入的结点一定要是红色的。那么,插入时就可能会产生颜色冲突,即插入结点 N 和 它的父节点 P 的颜色都是红色。如果 P 是黑色,没有冲突,直接插入就好。


颜色冲突问题解决
要明确,在红黑树插入节点 N 时,如果出现颜色冲突,一定需要关注它的叔叔节点 U,这时,自然要先找到 N 的祖父节点 GP。
情况分析
冲突时,N 和 P 都是红的,因为树本身原本是一颗红黑树,此时 GP 一定存在且为黑。分析 U 的情况:
—— U 红
———— P 和 U 涂黑,GP涂红,使 N=GP,然后去观察 N 和 N 的父亲 P 的情况,如果颜色冲突就重复这一步骤,如果不冲突,则下面的步骤也不需要进行。
—— U 黑或 U 不存在
———— 这时,N 和 P 都是红色,如果 N 和 P 不同边(N 和 P 一个是左孩子,一个是右孩子),则需要进行第一次的旋转操作(以 P 为中心旋转,旋转之后,N 和 P 同边),旋转之后,N 会在 P 的上面,使 N=P、P=N->parent。这时,将 P 涂黑,GP 涂红,然后以 GP 进行第二次的旋转,使红黑树重新平衡。
解释
U 红时进行的操作,不会改变红黑树根结点到叶结点的路径中黑色节点的个数,在操作过程中,可能不需要进行下一步的操作(旋转),就已经平衡了。
U 黑时进行的操作,此时可能需要以 P 进行旋转(左旋或右旋),第一次的旋转不会改变结点颜色,也不会改变路径中黑节点的个数,它是为了第二次的旋转准备的,因为第二次旋转必须是同边的。当 N 和 P 同边后,会对 P 和 GP 重新着色,着色以后,路径中黑节点的个数发生了变化,以 GP 进行的第二次旋转使得路径中黑色结点的个数恢复,由此可以知道,插入操作最多只会造成两次的旋转操作。
代码

enum COL{RED,BLACK};	//这里表示颜色
typedef int T;
typedef struct RBNode{
	T data;
	char col;
	struct RBNode *left;
	struct RBNode *rigth;
	struct RBNode *parent;
}RBNode;
typedef struct RBNode* RBTree;	//定义RBTree

//表示节点是左孩子还是右孩子
enum DIRE{L,R};

//创建一个结点,传入父节点地址,返回创建结点的指针
RBNode *rbtree_create_rbnode(T data,RBNode *parent){
	RBNode *node = malloc(sizeof(RBNode));
	if(node != NULL){
		node->col = RED;	//每次插入的结点都是红色的
		node->data = data;
		node->left = NULL;
		node->right = NULL;
		node->parent = parent;
	}
	return node;
}

/*以 node 为中心进行左旋,ptree是指向根节点的指针
    ||                     ||
   node                    nr 
   /  \          -->      /  \
  nl   nr               node  r 
      / \               / \
     l   r             nl  l
需要处理的有node->parent->right(或->left)、nr->left、nr->parent、l->parent、node->right、node->parent
*/
void left_rotation(RBTree *ptree,RBNode *node){
	RBNode *right = node->right;
//处理nr->parent
	right->parent = node->parent;
//处理node->parent->right(或->left)
	if(node->parent==NULL){	//node是根节点,旋转后nr成为根节点
		*ptree = right;
	}else{	//node不是根节点
		if(node == node->parent->left){
			node->parent->left = right;	
		}else{
			node->parent->right = right;	
		}
	}
//处理node->right
	node->right = right->left;
	if(node->right != NULL){
//处理l->parent
		node->right->parent = node;	
	}
//处理nr->left
	right->left = node;
//处理node->parent
	node->parent = right;
}

/*
      ||                     ||
     node                    nl
     /  \         -->       /  \
    nl  nr                 l   node  
   / \                          / \
  l   r                        r   nr
*/
void right_rotation(RBTree *ptree,RBNode *node){	
	RBNode *left = node->left;
	left->parent = node->parent;
	if(left->parent == NULL){
		*ptree = left;	
	}else{
		if(node == node->parent->left){
			node->parent->left = left;	
		}else{
			node->parent->right = left;	
		}
	}
	node->left = left->right;
	if(node->left != NULL){
		node->left->parent = node;	
	}
	node->parent = left;
	left->right = node;
}

//调节红黑树的平衡,ptree为根结点,node为插入时的结点
void rbtree_insert_repair(RBTree *ptree,RBNode *node){
	RBNode *parent = node->parent;
	//父节点存在且为红色
	while(parent != NULL && parent->col == RED){
		RBNode *gp = parent->parent;
		//node是父左还是父右
		int nd = node == parent->left?L:R;
		//parent是祖父左还祖父右
		int pd = parent == gp->left?L:R;
		RBNode *uncle = pd==L?gp->right:gp->left;
		//如果叔叔存在且为红
		if(uncle!=NULL && uncle->col == RED){
			parent->col = BLACK;
			uncle->col = BLACK;
			gp->col = RED;
			node = gp;
			parent = node->parent;	//调节时总是先利用 uncle(红) 解决 node 和 parent 的颜色冲突,所以 continue
			continue;
		}
		//这里开始,node 没有 uncle,或者 uncle 为黑
		if(nd != pd){//不同边  旋转为同边
			if(nd == L){
				right_rotation(ptree,parent);
			}else{
				left_rotation(ptree,parent);
			}
			node = parent;
			parent = node->parent;
		}
		//在这里,由于同边的红红冲突,N 和 P 为红,GP 黑
		gp->col = RED;
		parent->col = BLACK;
		//经过上述处理 同边 以 GP 旋转
		if(pd == L){//和父亲同左,在GP左边
			right_rotation(ptree,gp);
		}else{//和父亲同右,在GP右边
			left_rotation(ptree,gp);	
		}
		break;
}
	(*ptree)->col = BLACK;	//红黑树根节点必须是黑的,上数过程中,可能会将根节点染红
}
//插入结点 并 调节红黑树
int rbtree_insert(RBTree *ptree,T data,int (*compar)(T,T)){
	RBTree *proot = ptree;
	RBNode *parent = NULL;
	while(*ptree != NULL){
		parent = *ptree;
		int ret = compar(data,(*ptree)->data);
		if(ret < 0){
			ptree = &(*ptree)->left;	
		}else if(ret > 0){
			ptree = &(*ptree)->right;	
		}else{	//等于时不插入
			return -1;	
		}
	}
	*ptree = rbtree_create_rbnode(data,parent);
	if(*ptree == NULL){
		return -1;	
	}
	rbtree_insert_repair(proot,*ptree);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值