红黑树的自旋转和结点插入操作的解析

红黑树可以实现自平衡,主要依靠于它的旋转和变色的特性

//红黑树节点
typedef struct RBTreeNode
{
	unsigned char color;
	Type key;
	struct RBTreeNode* left;
	struct RBTreeNode* right;
	struct RBTreeNode* parent;
}Node, *RBTree;

//红黑树的根
typedef struct rb_root
{
	Node* node;
}RBRoot;

左旋:以某个节点作为旋转节点,其右子节点变为旋转节点的父节点,右子节点的左子节点变为旋转节点的右子节点,旋转节点的左子节点保持不变。

static void rbtree_left_rotate(RBRoot* root, Node* x)
{
	//设置x的右孩子为y
	Node* y = x->right;
	//将y的左孩子设置为x的右孩子
	//如果y的左孩子非空,将x设为y的左孩子的父亲
	x->right = y->left;
	if (y->left != NULL)
	{
		y->left->parent = x;
	}
	//将x的父亲设为y的父亲
	 y->parent = x->parent;
	//if语句成立说明x为根节点
	if (x->parent == NULL)  //修改红黑树的根节点
	{
		root->node = y;   //如果“x的父亲”是空节点,则将y设为根节点
	}
	else 
	{   //如果x是它父节点的左孩子,则将y设为“x的父节点”
		if (x->parent->left == x)
		{
			x->parent->left = y;
		}  //如果x是它父节点的右孩子,则将y设为“x的父节点”
		else
		{
			x->parent->right = y;
		}
	}
	//将x设为y的左孩子
	y->left = x;
	//将x的父节点设为y
	x->parent = y;
}

右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。

static void rbtree_right_rotate(RBRoot* root, Node* y)
{
	//设置x是当前节点的左孩子
	Node* x = y->left;
	//将x的右孩子设为y的左孩子
	//如果x的右孩子不为空,将y设为x的右孩子的父亲
	y->left = x->right;
	if (x->right != NULL)
	{
		x->right->parent = y;
	}
	//将y的父亲设置为x的父亲
	x->parent = y->parent;
	//if语句成立说明y为根节点
	if (y->parent == NULL)
	{
		root->node = x;     //如果“y的父亲”是空节点,则将x设为根节点
	}
	else
	{   //如果y是它父节点的右孩子,则将x设为“y的父节点”
		if (y->parent->right == y)
		{
			y->parent->right = x;
		}  //如果y是它父节点的左孩子,则将x设为“y的父节点”
		else
		{
			y->parent->left = x;
		}
	}
	//将y设为x的右孩子
	x->right = y;
	//将y的父节点设为x
	y->parent = x;
}

红黑树的插入操作
情况1:红黑树为空树
直接把插入节点作为根节点,把颜色涂成黑色即可。

void insert_case1(node *n){
	if(n->parent == NULL)
		n->color = BLACK;
	else
	 	insert_case2 (n); 
}

情况2:插入节点的父节点为黑色节点
因为插入节点是红色的,当插入节点的父节点为黑色节点时,不会对红黑树的平衡造成影响,直接插入即可。

void insert_case2(node *n){
	if(n->parent->color == BLACK)
		return;
	else
		insert_case3 (n);
}

情况3:插入节点的父节点为红色节点。如果插入的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。

情况3_1:叔叔节点存在且为红色

此时的处理方法是将父亲节点和叔叔节点都涂成黑色,将祖父节点涂成红色,再将祖父节点设置成当前插入的节点,继续从底部向上进行递归或循环判断。

void insert_case3_1(node *n){
if(uncle(n) != NULL && uncle (n)->color == RED) 
{
	n->parent->color = BLACK;
	uncle (n)->color = BLACK;
	grandparent (n)->color = RED;
	insert_case1(grandparent(n));
}
else
	insert_case3_2and3_3(n);
}

情况3_2:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的左子节点

情况3_2_1:插入节点是父节点的左子节点
处理方法是将父亲节点设为黑色,祖父节点设为红色,并对祖父节点进行右旋

情况3_3:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的右子节点

情况3_3_1:插入节点是父节点的右子节点
处理方法是将父亲节点设为黑色,祖父节点设为红色,并对祖父节点进行左旋

void insert_case3_2_1and3_3_1(node *n)
{
	n->parent->color = BLACK;           
	grandparent (n)->color = RED;
	if(n->parent == grandparent(n)->left && n == n->parent->left) 
	{
		rotate_right(grandparent(n));    //情况3_2_1
	}
	else if(n->parent == grandparent (n)->right && n == n->parent->right)
	{
		rotate_left(grandparent(n));     //情况3_3_1
	}
}

情况3_2:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的左子节点

情况3_2_2:插入节点是父节点的右子节点
处理方法是对父节点进行左旋,然后情况就变成了3-2-1的情况

情况3_3:叔叔节点不存在或为黑色节点,并且插入节点的父亲节点是祖父节点的右子节点

情况3_3_2:插入节点是父节点的左子节点
处理方法是对父节点进行右旋,然后情况就变成了3_3_1的情况

void insert_case3_2_2and3_3_2(node *n)
{
	if(n->parent == grandparent(n)->left &&  n == n->parent->right) 
	{
		rotate_left(n->parent);   //情况3_2_2
		n = n->left;
	} 
	else if(n->parent == grandparent(n)->right  && n == n->parent->left) 
	{
		rotate_right(n->parent);  //情况3_3_2
		n = n->right;   
	}
}

完整代码如下:

#define rb_parent(r)        ((r)->parent)
#define rb_color(r)         ((r)->color)
#define rb_is_red(r)        ((r)->color == RED)
#define rb_is_black(r)      ((r)->color == BLACK)
#define rb_set_black(r)     do {(r)->color = BLACK;} while(0)
#define rb_set_red(r)       do {(r)->color = RED;} while(0)
#define rb_set_parent(r, p) do {(r)->parent = (p);}while(0)
#define rb_set_color(r, c)  do {(r)->color = (c);}while(0)
static void rbtree_insert(RBRoot* root, Node* node)
{
	//添加节点:将节点node插入红黑树中,root为红黑树的根,node为插入的节点
	Node* y = NULL;
	Node* x = root->node;   //先将根节点给x

	//1、将红黑树当做一颗二叉查找树,将节点添加到二叉查找树中
	while (x != NULL)
	{
		y = x;   //y为所需插入节点的父节点
		if (node->key < x->key)
		{
			x = x->left;
		}
		else 
		{
			x = x->right;
		}
	}
	rb_parent(node) = y;   //找到父节点并把要插入节点的父节点的指针修改
	//修改父节点的子节点指针
	if (y != NULL)
	{
		if (node->key < y->key)
		{
			y->left = node;  //情况2:node包含的值 < y所包含的值
		}
		else
		{
			y->right = node;   //情况3:node包含的值 >= y所包含的值
		}
	}
	else
	{
		root->node = node;    //情况1:若y是空节点,则将node设为根
	}

	//2、设置节点的颜色为红色
	node->color = RED;

	//3、将它重新修正为一颗rb树
	rbtree_insert_fixup(root, node);
}
static void rbtree_insert_fixup(RBRoot* root, Node* node)
{
	Node* parent, * gparent;
	//若父节点存在,且父节点的颜色是红色
	while ((parent = rb_parent(node)) && rb_is_red(parent))
	{
		gparent = rb_parent(parent);

		//若父节点是祖父节点的左孩子
		if (parent == gparent->left)
		{
			//Case1条件:叔叔节点是红色
			{
				Node* uncle = gparent->right;
				if (uncle && rb_is_red(uncle)) //若没有节点进入该分支,如何构造
				{
					rb_set_black(uncle);
					rb_set_black(parent);
					rb_set_red(gparent);
					node = gparent;
					continue;
				}
			}
			//Case2条件:叔叔是黑色,且当前节点是右孩子,叔叔不存在,也认为是黑色
			if (parent->right == node)    //插入80节点时,先左旋,后右旋
			{
				Node* tmp;
				rbtree_left_rotate(root, parent);
				tmp = parent;
				parent = node;
				node = tmp;
			}
			//Case3条件:叔叔是黑色,且当前节点是左孩子
			rb_set_black(parent);
			rb_set_red(gparent);
			rbtree_right_rotate(root, gparent);
		}
		else 
		{  //若父亲节点是祖父节点的右孩子

			{ //Case1条件:叔叔节点是红色
				Node* uncle = gparent->left;
				if (uncle && rb_is_red(uncle))
				{
					rb_set_black(uncle);
					rb_set_black(parent);
					rb_set_red(gparent);
					node = gparent;
					continue;   //继续进行调整
				}
			}
			//Case2条件:叔叔是黑色或空缺,且当前节点是左孩子,插入30时,先右旋,后左旋
			if(parent->left == node)
			{
				Node* tmp;
				rbtree_right_rotate(root, parent);
				tmp = parent;
				parent = node;
				node = tmp;
			}
			//Case3条件:叔叔是黑色或空缺,且当前节点是右孩子
			rb_set_black(parent);   //旋转前设置好颜色
			rb_set_red(gparent);
			rbtree_left_rotate(root, gparent);
		}
	}
	//将根节点设为黑色
	rb_set_black(root->node);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值