【c++】手撕红黑树

目录

一、什么是红黑树

二、红黑树的基本框架

三、红黑树的插入


一、什么是红黑树

1.红黑树是在AVL树的基础上进行改进的一种树,也是一颗二叉搜素树,它并不要求像AVL树一样,严格要求平衡因子的绝对值不大于1,它只要求最长路径不超过最短路径的2倍即可,正如它的名字一样,它不再使用平衡因子,而是用颜色来标记每一个节点,分别是红色和黑色。

2、红黑树的根节点必须是黑色节点

3、红黑树不能有两个连续的红节点,但可以有连续的黑色节点。

4、红黑树的叶子节点必须是黑色,这里的叶子节点说的是NIL。

5、红黑树每条路径的黑色节点数目相等

6ec93b96ab0c4c9998fc9173947e7362.png

这里的NIL通常我们认为就是存放nullptr的地方。


二、红黑树的基本框架

基本的成员,这里颜色我们可以使用一个枚举来实现。

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

template<class K, class V>
struct RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
private:
	Node* _root = nullptr;

三、红黑树的插入

我们先默认插入的节点的颜色为红色。

首先先像普通的二叉搜索树一样,通过左右比较来找到我们要插入的位置,比要插入的值大,就往左边走,比要插入的值小,就往右边走,和要插入的值一样,就break出来,最后来到空位置,将值插入进去。

bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_cor = BLACK;
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		//寻找插入的位置
		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}

		//插入新节点
		cur = new Node(kv);
		cur->_parent = parent;
		cur->_cor = RED;

		//链接cur
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
}

然后再调整我们的颜色就可以了

这里分为几种情况

首先我们先理解几个定义出来的变量,

cur为我们当前的节点

parent为我们当前节点的父节点

grandfather为parent节点的父节点

uncle为parent节点的叔叔节点,也就是grandfather节点的左或者右孩子

1ef1a1b937f04b39a0116955f5758324.png

1.如果parent节点的颜色为黑色,我们就结束

因为我们默认插入的节点为红色,parent节点为黑色,那么就符合红黑树的性质,没有与性质发生冲突。

2.如果parent节点存在且为红,uncle节点不存在

解决方法:旋转+变色

8aa0a464a8804e5f8991972377599460.png

如果cur == parent->left, parent在grandfather的左边,那么就将grandfather右旋,然后把cur的颜色和grandfather的颜色调成红色,parent的颜色为黑色,然后结束。

如果cur == parent->right,parent == grandfather->grandfather的左边,那么就先左旋parent,再右旋grandfather。

ad55fbf275814429bfd4de7ac3ce0b33.png

3.parent为红,uncle存在且为红

解决方法:将parent和uncle变为黑,grandfather变为红,然后继续向上处理

929ee96c78fe42b1b95581c595559b63.png

64c98d6e399a42c7b9fe060a60984ab2.png

然后就继续变成的刚才的情况,继续处理,这里要注意,当处理到根节点时,如果根节点刚好是处理完变成红的,要手动处理成黑色

4.parent为红,uncle为黑

这种情况通常发生在我们继续向上处理的过程中出现的一种情况

9ed4f0c4da274cd6b8720b6a04d5d024.png

解决方法:和uncle为黑的解决方法一样,旋转+变色

c1f3776ee0dd4a9c9311ccf2a9681a98.png

77972453546c421d9e06dede88f31228.png

这里旋转要注意路径是直线还是折线,要是折线就要用双旋

fe2a957d5935470589c7d8c6b4edc757.png

这是总体调整颜色的代码

while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // u不存在 或 存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g
						//   p
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//		c
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						// g
						//	  p
						//       c
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						// g
						//	  p
						// c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

这是总体插入的代码

bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // u不存在 或 存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g
						//   p
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//		c
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						// g
						//	  p
						//       c
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						// g
						//	  p
						// c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return true;
	}
void RotateL(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		parent->_right = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}

		cur->_left = parent;

		Node* ppnode = parent->_parent;

		parent->_parent = cur;


		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;

			}

			cur->_parent = ppnode;
		}
	}


	void RotateR(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_left;
		Node* curright = cur->_right;

		parent->_left = curright;
		if (curright)
			curright->_parent = parent;

		Node* ppnode = parent->_parent;
		cur->_right = parent;
		parent->_parent = cur;

		if (ppnode == nullptr)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}

			cur->_parent = ppnode;
		}
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ck837

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值