当前位置:首页 > 芯闻号 > 充电吧
[导读]linux内存管理模块中使用红黑树算法来提升虚拟内存查找速度,源码请参考linux内核目录下rbtree.c文件。红黑树算法原理在阅读红黑树算法源代码之前最好先了解红黑树原理。rb_insert_co

linux内存管理模块中使用红黑树算法来提升虚拟内存查找速度,源码请参考linux内核目录下rbtree.c文件。

红黑树算法原理

在阅读红黑树算法源代码之前最好先了解红黑树原理。

rb_insert_color函数详解:

void rb_insert_color(rb_node_t * node, rb_root_t * root)
{
	rb_node_t * parent, * gparent;

	while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
	{
		gparent = parent->rb_parent;

		if (parent == gparent->rb_left)
		{
			{
				register rb_node_t * uncle = gparent->rb_right;
				if (uncle && uncle->rb_color == RB_RED)
				{
					uncle->rb_color = RB_BLACK;
					parent->rb_color = RB_BLACK;
					gparent->rb_color = RB_RED;
					node = gparent;
					continue;
				}
			}

			if (parent->rb_right == node)
			{
				register rb_node_t * tmp;
				__rb_rotate_left(parent, root);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			parent->rb_color = RB_BLACK;
			gparent->rb_color = RB_RED;
			__rb_rotate_right(gparent, root);
		} else {
			{
				register rb_node_t * uncle = gparent->rb_left;
				if (uncle && uncle->rb_color == RB_RED)
				{
					uncle->rb_color = RB_BLACK;
					parent->rb_color = RB_BLACK;
					gparent->rb_color = RB_RED;
					node = gparent;
					continue;
				}
			}

			if (parent->rb_left == node)
			{
				register rb_node_t * tmp;
				__rb_rotate_right(parent, root);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			parent->rb_color = RB_BLACK;
			gparent->rb_color = RB_RED;
			__rb_rotate_left(gparent, root);
		}
	}

	root->rb_node->rb_color = RB_BLACK;
}

注解:

    函数参数

        node:新插入的结点。

        root:红黑树的根结点。

    第5行:往红黑树中插入结点时,总是将新插入结点颜色设置为红色。当新结点的父结点不存在或者父结点颜色为黑色时,不需要做红黑树调整直接跳到第63行。

    第7行:获取node结点的祖父结点。

    (第9-35行 处理node父结点是其祖父结点左子结点的情况)

    第12行:获取node的叔父结点。

    第13-20行:当叔父结点存在并且颜色为红色,就将node的父结点与叔父结点的颜色设成黑色,并且将node的祖父结点颜色设成红色。现在祖父结点的颜色为红色,但祖父结点的父结点也可能为红色,不满足红黑树性质,因此将祖父结点当做新插入的结点重新进行红黑树调整。

    第23-30行:叔父结点不存在或者颜色为黑色,此时node的祖父结点颜色必为黑色(因为node父结点颜色为红色),针对node的父结点进行一次左旋转,让父结点成为node的左子结点,并交换父结点与node结点位置,使以前的父结点成为新插入的结点。

    第32-34得:将父结点的颜色设为黑色(此时新node的颜色还是红色),将祖父结点的颜色设为红色。再针对祖父结点进行一次右旋转,此时node拥有一个黑色的父结点及红色的兄弟结点。

    (第36-61行处理node的父结点是其祖父结点的右子结点的情况)

    第37行:获取node的叔父结点。

    第38行:当node的叔父结点存在并且颜色为红色,就将node的父结点与叔父结点设置为黑色,并将node的祖父结点设置为红色。再将祖父结点当做新插入的结点重新进行红黑树调整。

    第48-55行:如果node结点是其父结点的左子结点,就针对node的父结点进行一次右旋转,让其父结点成为node的右子结点,并交换父结点与node的位置,让以前的父结点成为新插入的结点

    第57-59行:将父结点的颜色设为黑色(此时新node的颜色还是红色),将祖父结点的颜色设置为红色。再针对祖父结点进行一次左旋转,此时node拥有一个黑色的父结点及红色的兄弟结点。

    第63行:将根结点的颜色设为黑色。这一步是有必要的,当新插入的结点为根结点或者在树调整过程中将根结点颜色变成了红色,这一行将根结点的颜色设成黑色。

 rb_erase函数详解:

void rb_erase(rb_node_t * node, rb_root_t * root)
{
	rb_node_t * child, * parent;
	int color;

	if (!node->rb_left)
		child = node->rb_right;
	else if (!node->rb_right)
		child = node->rb_left;
	else
	{
		rb_node_t * old = node, * left;

		node = node->rb_right;
		while ((left = node->rb_left))
			node = left;
		child = node->rb_right;
		parent = node->rb_parent;
		color = node->rb_color;

		if (child)
			child->rb_parent = parent;
		if (parent)
		{
			if (parent->rb_left == node)
				parent->rb_left = child;
			else
				parent->rb_right = child;
		}
		else
			root->rb_node = child;

		if (node->rb_parent == old)
			parent = node;
		node->rb_parent = old->rb_parent;
		node->rb_color = old->rb_color;
		node->rb_right = old->rb_right;
		node->rb_left = old->rb_left;

		if (old->rb_parent)
		{
			if (old->rb_parent->rb_left == old)
				old->rb_parent->rb_left = node;
			else
				old->rb_parent->rb_right = node;
		} else
			root->rb_node = node;

		old->rb_left->rb_parent = node;
		if (old->rb_right)
			old->rb_right->rb_parent = node;
		goto color;
	}

	parent = node->rb_parent;
	color = node->rb_color;

	if (child)
		child->rb_parent = parent;
	if (parent)
	{
		if (parent->rb_left == node)
			parent->rb_left = child;
		else
			parent->rb_right = child;
	}
	else
		root->rb_node = child;

 color:
	if (color == RB_BLACK)
		__rb_erase_color(child, parent, root);
}

注解:

    函数参数:

        node: 将要删除的结点

        root: 红黑树的根结点

    (第6-9行处理被删结点至多只有一个子结点的情况,并找出不为空的子结点)

    第6-7行:判断node的左子结点是否存在,如果不存在child针指就指向右子结点(右子结点也可能不存在)。

    第8-9行:判断node的右子结点是否存在,如果不存在child指针就指向其左子结点。

    (第11-53行 处理被删结点有两个非空子结点的情况。当结点存在两个非空子结点时就找出其左子树中元素最大的结点或者右子树中元素最小的结点,并将该结点替换到将要被删除的结点位置上,再删除元素最大或者最小的结点。这时就将删除有两个非空子结点的问题变成删除至多只有一个非空子结点的问题。linux内存管理模块中的红黑树是按结点所在的内存首地址来排序存储的,如父结点的内存首地址一定会大于其左子结点首地址且小于右子结点首地址,因此在替换结点时只替换结点的位置不能改变结点的首地址)。

    第12行:保存node到old变量中。

    第14-16行:找出node的右子树中首地址最小的结点。如果找到就将该结点当做将要被删除的结点。

    第17行:获取node的右子结点,经过第14-16行的处理现在node结点的首地址最小,所以node不可能存在左子结点,但右子结点可能会存在。

    第18行:获取node的父结点。

    第19行:获取node的颜色属性。

    第21-22行:如果node的子结点存在,就设置其子结点的父结点为node的父结点(因为node结点将要被删除)。

    第23-29行:如果node的父结点存在,当node是其父结点的左子结点时就设置node的子结点为其父结点的左子结点,否则就设置为其父结点的右子结点。

    第30-31行:这种情况不可能会发生。

    第33-34行:如果node等于old,说明old(old中保存有rb_erase函数中node参数的值,即要被替换的结点)的右子结点没有子结点,将父结点指向node结点(当node等于old时,在node替换old之后,node与其子结点的关系保持不变)。

    (第35-38行 这里将node结点替换掉old结点)

    第35行:设置node的父结点为old结点的父结点。

    第36行:将old结点颜色替换到node结点中。

    第37行:将node的右子结点设置为old的右子结点。

    第38行:将node的左子结点设置为old的左子结点。

    第40-46行:如果old的父结点存在,当old是其父结点的左子结点时就将node变成其父结点的左子结点,否则为其父结点的右子结点。

    第47行:如果old为根结点就设置node为根结点。

    第49行:将node设置为old左子结点的父结点。

    第50-51行:如果old的右子结点存在,设置node为old右子结点的父结点。

    第52行:跳转到70行进一步处理。

    (第55-68行 进一步处理被删结点至多只有一个非空子结点的情况)

    第55-56行:获取node的父结点及颜色。

    第58-59行:如果node的子结点存在,就设置其子结点的父结点为node的父结点。

    第60-66行:node的父结点存在且node是其父结点的左子结点时就设置node的子结点为父结点的左了结点,否则为父结点的右子结点。

    第67-68行:当node为根结点时,删除node后,其子结点成为根结点。

    第70-72行:如果被删结点颜色为红色,此时不影响红黑树性质,否则调用__rb_erase_color函数进一步处理。

    当rb_erase函数删除有两个非空子结点的结点时比较复杂,难以理解。下面提供两张删除结点的示例图便于理解,本想提供flash动画的,但不知道怎么发到博客上来。

     示例1:假设被删结点为父结点的左子结点,且被删结点的右子结点没有子结点

    示例2:假设被删结点是父结点的右子结点,且被删结点的右子结点存在两个非空子结点

__rb_erase_color函数详解:


static void __rb_erase_color(rb_node_t * node, rb_node_t * parent,
			     rb_root_t * root)
{
	rb_node_t * other;

	while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
	{
		if (parent->rb_left == node)
		{
			other = parent->rb_right;
			if (other->rb_color == RB_RED)
			{
				other->rb_color = RB_BLACK;
				parent->rb_color = RB_RED;
				__rb_rotate_left(parent, root);
				other = parent->rb_right;
			}
			if ((!other->rb_left ||
			     other->rb_left->rb_color == RB_BLACK)
			    && (!other->rb_right ||
				other->rb_right->rb_color == RB_BLACK))
			{
				other->rb_color = RB_RED;
				node = parent;
				parent = node->rb_parent;
			}
			else
			{
				if (!other->rb_right ||
				    other->rb_right->rb_color == RB_BLACK)
				{
					register rb_node_t * o_left;
					if ((o_left = other->rb_left))
						o_left->rb_color = RB_BLACK;
					other->rb_color = RB_RED;
					__rb_rotate_right(other, root);
					other = parent->rb_right;
				}
				other->rb_color = parent->rb_color;
				parent->rb_color = RB_BLACK;
				if (other->rb_right)
					other->rb_right->rb_color = RB_BLACK;
				__rb_rotate_left(parent, root);
				node = root->rb_node;
				break;
			}
		}
		else
		{
			other = parent->rb_left;
			if (other->rb_color == RB_RED)
			{
				other->rb_color = RB_BLACK;
				parent->rb_color = RB_RED;
				__rb_rotate_right(parent, root);
				other = parent->rb_left;
			}
			if ((!other->rb_left ||
			     other->rb_left->rb_color == RB_BLACK)
			    && (!other->rb_right ||
				other->rb_right->rb_color == RB_BLACK))
			{
				other->rb_color = RB_RED;
				node = parent;
				parent = node->rb_parent;
			}
			else
			{
				if (!other->rb_left ||
				    other->rb_left->rb_color == RB_BLACK)
				{
					register rb_node_t * o_right;
					if ((o_right = other->rb_right))
						o_right->rb_color = RB_BLACK;
					other->rb_color = RB_RED;
					__rb_rotate_left(other, root);
					other = parent->rb_left;
				}
				other->rb_color = parent->rb_color;
				parent->rb_color = RB_BLACK;
				if (other->rb_left)
					other->rb_left->rb_color = RB_BLACK;
				__rb_rotate_right(parent, root);
				node = root->rb_node;
				break;
			}
		}
	}
	if (node)
		node->rb_color = RB_BLACK;
}

注解:

    函数参数:

        node:被删结点的子结点

        parent:被删结点的父结点

        root:红黑树根结点

    第6行:如果node是根结点或者其颜色为红色while循环结束(调用__rb_erase_color函数的条件是被删结点颜色为黑色,直接将被删结点的子结点node置为黑色就满足红黑树性质)。

    (第8-47行 处理node是其父结点的左子结点情况)

    第10行:获取node的兄弟结点,保存到other变量中。

    第11-17行:当other结点颜色为红色时(other父结点颜色必为黑),将其父结点置为红色,other结点置为黑色。并针对其父结点进行一次左旋转让other的父结点成为它的左子结点(此种情况的详细步骤请参考上面提起过的红黑树原理网站中删除结点的情况2),并从新获取node的兄弟结点。

    第18-26行:如果other的两个子结点都为黑色,这时就将other置为红色。重新在node的父结点上做红黑树调整(参考红黑树原理网站中删除结点的情况4)。

    (第28-46行 处理other的子结点颜色为红色的情况)

    第29-38行:当other的右子结点为黑色时(此时other的左子结点必为红色),将other的左子结点置为黑色,other置为红色并针对other进行一次右旋转,使other的左子结点成为other的父结点(参考红黑树原理网站中删除结点的情况5),重新获取node的兄弟结点,保存到other变量中。

    第39-44行:设置other的颜色为其父结点的颜色,将父结点的颜色置为黑色,设置other的右子结点颜色为黑色并针对node的父结点进行一次左旋转(参考红黑树原理网站中删除结点的情况6)。

    第49-87行:这里的处理步骤与8-47行类似。

    第89-90行:将根结点置为黑色。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭