红黑树的原理及其简单实现

短期实习比较闲,摸鱼撸个RBT

红黑树原理

首先阐述红黑树(RBT)的原理,RBT在二叉排序树(BST)的基础上又添加了下述的五条约束:

  1. 每个节点有颜色属性,要么是红色,要么是黑色。
  2. 根节点必为黑色
  3. 叶子节点下的空节点视为黑色
  4. 红色节点的左右孩子必为黑色节点
  5. 从红黑树上的任意一个节点出发到任意一个叶子节点的简单路径上,黑色节点的个数一致。(黑高相同)

需要说明的是,很多博客中会告诉你叶子节点必须是黑色,实际上它指的是颜色是黑色的NIL空节点,本质上就是叶子节点两侧的空节点是黑色,从而方便理解。实现过程中,叶子节点的左右孩子依旧设置为空节点,不特地定义一个NIL节点更加方便

具体的定义如下所示:

//RBT结构定义
//通过枚举来定义字面常量会更加方便
enum class Color {
	Red,Black
};
class TreeNode {
public:
	shared_ptr<TreeNode> left;
	shared_ptr<TreeNode> right;
	//通过在树的结构中添加父节点,总而方便得到祖父节点与,叔父节点
	shared_ptr<TreeNode> father;
	Color color;
	int value;
	TreeNode(Color col, int v = 0) :color(col), value(v) {
		left = nullptr;
		right = nullptr;
		father = nullptr;
	}
};

红黑树的插入

为了保证黑高一致的性质,插入节点必须是红色节点,但仍然会破坏根节点是黑色节点,以及红色节点只有黑色孩子这两个性质。需用类似AVL的旋转进行调整,插入为左右孩子是对称的,这里以插入为左孩子为例,策略如下:

  1. 当插入节点为根节点时,直接将插入节点涂黑即可。

  2. 当插入节点的父亲节点为黑色节点时,无需调整。

  3. 当插入节点的父亲节点为红色节点时,作如下调整:

    a. 如果插入节点的叔父节点为红色节点,那么将插入节点的父亲节点与其叔父节点涂黑,将祖父节点涂红,并将祖父节点视为插入节点。

    b. 如果插入节点的叔父节点为黑色节点,再分为两种情况。如果被插入节点与父亲节点方向一致(eg.同为左孩子),将父亲节点涂黑,祖父节点涂红,并且祖父节点左旋。如果方向不一致,那么先右旋父亲节点,将插入节点与其父亲节点的方向调整一致后,再作相同操作即可。

实现过程中有一些细节:

  1. 对于根节点来说,父亲节点为空的节点即为根节点。
  2. 与AVL的左右旋操作不同,RBT的左右旋需要更新父节点信息。

具体的实现如下所示:

//左右旋操作
//一定注意,与avl的左右旋不同,rbt的左右旋还需要更新父亲节点
void LeftRotate(shared_ptr<TreeNode>& node)
{
	shared_ptr<TreeNode> right = node->right;
	shared_ptr<TreeNode> init_father = node->father;
	node->right = right->left;
	right->left = node;

	node->father = right;
	right->father = init_father;

	node = right;
}
void RightRotate(shared_ptr<TreeNode>& node)
{
	shared_ptr<TreeNode> left = node->left;
	shared_ptr<TreeNode> init_father = node->father;
	node->left = left->right;
	left->right = node;

	node->father = left;
	left->father = init_father;

	node = left;
}

//红黑树本身依旧满足BST的性质
//红黑树的插入
void Insert(shared_ptr<TreeNode>& node, shared_ptr<TreeNode>& father,int val)
{
	//根节点的父节点一定是空节点
	if (node==nullptr)
	{
		if (father == nullptr)
			node = make_shared<TreeNode>(Color::Black, val);
		else
		{
			shared_ptr<TreeNode> tpNode= make_shared<TreeNode>(Color::Red, val);
			tpNode->father = father;
			node = tpNode;
		}
		return;
	}
	//左侧插入
	if (val < node->value)
	{
		Insert(node->left, node,val);
		shared_ptr<TreeNode> uncle = ((node == father->left) ? father->right : father->left);
		if (node->color == Color::Red)
		{
			//叔父为空节点时,是视作黑色的
			if (uncle!=nullptr && uncle->color == Color::Red)
			{
				node->color = Color::Black;
				uncle->color = Color::Black;
				if (father->father == nullptr)
					father->color = Color::Black;
				else
					father->color = Color::Red;
			}
			else
			{
				if (node == father->left)
				{
					node->color=Color::Black;
					father->color = Color::Red;
					RightRotate(father);
				}
				else
				{
					LeftRotate(node);
					node->color = Color::Black;
					father->color = Color::Red;
					RightRotate(father);
				}
			}
		}
	}//右侧插入
	else if (val > node->value)
	{
		Insert(node->right, node, val);
		shared_ptr<TreeNode> uncle = ((node == father->left) ? father->right : father->left);
		if (node->color == Color::Red)
		{
			if (uncle != nullptr && uncle->color == Color::Red)
			{
				node->color = Color::Black;
				uncle->color = Color::Black;
				if (father->father == nullptr)
					father->color = Color::Black;
				else
					father->color = Color::Red;
			}
			else
			{
				if (node == father->right)
				{
					node->color = Color::Black;
					father->color = Color::Red;
					LeftRotate(father);
				}
				else
				{
					RightRotate(node);
					node->color = Color::Black;
					father->color = Color::Red;
					LeftRotate(father);
				}
			}
		}
	}
}

待更新RBT的删除

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值