C++ 学习日志 · 红黑树

目录

红黑树:

模拟红黑树(三叉链):

insert:


红黑树:

红黑树 就和他的名字一样,树中只存在两种颜色的节点,他的本质也是一种二叉搜索树,它相对于使用平衡因子控制的二叉树,它会稍微宽松一些,控制平衡因子绝对值不超过1会比较严格也就意味着会有更多的旋转

红黑树的性质:

         ① 根节点是黑色

         ② 没有连续的红节点,红节点下一个节点必是黑节点

         ③ 每个节点的每条路径的都有相同数量的黑色节点

         ④ 每个叶子节点(空节点)都是黑色节点 

综合以上的性质,可以得出新的性质:

        最长路径的节点个数不会超过最短路径的节点个数二倍 

        最短路径:全是黑色节点

        最长路径:一黑一红交叉 

模拟红黑树(三叉链):

insert:

红黑树和平衡二叉树相比,没有平衡二叉树那么严格,旋转的更少,但是既然要插入一个值,它的位置是可以根据子节点 左小右大 来确定,那它的颜色该如何选择呢?

        颜色的选择就要回归到红黑的性质(规则)上面 :

        如果插入的是红色节点,那么有可能破坏第②个规则

        如果插入的是黑色节点,那么就一定会破坏第③个规则,这样涉及新增节点的路径上的黑色

        节点数量就会比其他的路径的黑色节点数量多一个

综上得出结论:插入的节点得是一个红色节点。 (毕竟这样我们还能"补救")

那大家看现在这种情况应该怎么调整颜色呢?

        调整颜色的同时也需要不破坏红黑树的规则:

        首先保证没有连续的红色节点,那么那么新节点的父亲节点就需要变成黑色,此时路径上的黑色节点的数量不统一,那么叔叔节点也应该变成黑色,

        此时,最上面的爷爷节点,就要判断是否是根节点,如果是根节点则为黑色(规则第一条),不是则变为红色继续向上遍历   (如果此时爷爷不是根节点还保持黑色不变的话,那么图片中这一小的分支路径上面就会多出来一个节点)

bool insert(const pair<K, V>& data)
	{
		if (_root == nullptr)
		{
			_root = new node(data);
			_root->col = BLACK;
			return;
		}

		node* parent = nullptr;
		node* cur = _root;

		while (cur)
		{
			if (cur->_key > data)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < data)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		//插入新节点
		node* NewNode = new node(data);
		if (parent->_key > data)
		{
			parent->_left = NewNode;
		}
		else
		{
			parent->_right = NewNode;
		}

		cur = NewNode;
		while (parent && parent->col == RED)
		{
			node* groundnode = parent->_parent;
			if (groundnode->_left == parent)
			{
				node* unclenode = groundnode->_right;
				if (unclenode && unclenode == RED)
				{
					parent->col = unclenode->col = BLACK;
					groundnode->col = RED;

					cur = groundnode;
					parent = cur->_parent;
				}

			}
			else
			{
				node* unclenode = groundnode->_left;
			}
		}
	}

注意:颜色变化根子节点是父节点的左右没有关系

 之前讨论的是叔叔节点存在且为黑的情况,那如果现在叔叔节点不存在呢


这个时候就需要进行旋转了 


注意:这个菱形代表的子节点的多种情况


现在如果叔叔节点,存在并且是黑色的呢? 

大家看这张图,就能明白有的时候一种情况,多数是由其他情况在调整的时候演变出来的上图就是叔叔节点存在且为黑的情况,那么此时应该怎么做呢?


当然这个是右单旋(儿子节点、父亲节点它俩都是各自父亲节点的左节点),左单选的原理和这个是一样的.

bool insert(const pair<K, V>& data)
	{
		if (_root == nullptr)
		{
			_root = new node(data);
			_root->col = BLACK;
			return;
		}

		node* parent = nullptr;
		node* cur = _root;

		while (cur)
		{
			if (cur->_key > data)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < data)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		//插入新节点
		node* NewNode = new node(data);
		if (parent->_key > data)
		{
			parent->_left = NewNode;
		}
		else
		{
			parent->_right = NewNode;
		}

		cur = NewNode;
		while (parent && parent->col == RED)
		{
			node* groundnode = parent->_parent;
			if (groundnode->_left == parent)
			{
				node* unclenode = groundnode->_right;
				if (unclenode && unclenode == RED)
				{
					parent->col = unclenode->col = BLACK;
					groundnode->col = RED;

					cur = groundnode;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_left == cur)
					{
						RotateR(groundnode);
						parent->col = BLACK;
						groundnode->col = RED;
					}
					else
					{
						RtotateL(parent);
						RtotateR(groundnode);
						cur->col = BLACK;
						groundnode->col = RED;
					}

					break;
				}
			}
			else
			{
				node* unclenode = groundnode->_left;
				if (unclenode && unclenode == RED)
				{
					parent->col = unclenode->col = BLACK;
					groundnode->col = RED;

					cur = groundnode;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_right == cur)
					{
						RotateL(groundnode);
						parent->col = BLACK;
						groundnode->col = RED;
					}
					else
					{
						RtotateR(parent);
						RtotateL(groundnode);
						cur->col = BLACK;
						groundnode->col = RED;
					}

					break;
				}
			}
		}
	}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值