数据结构---红黑树

本文详细介绍了红黑树的概念,包括其性质和定义。在红黑树的插入操作中,重点阐述了如何维持红黑树的平衡,包括三种插入后可能的情况及其对应的调整策略。此外,还提供了检测红黑树是否符合性质的两种方法,分别是检查连续红节点和路径上黑节点数量的一致性。
摘要由CSDN通过智能技术生成

介绍

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的


性质:

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

满足每条路径的黑节点相同情况下:

  1. 路径最短:全为黑
  2. 路径最长:黑红交替

在这里插入图片描述
所以在维护红黑树的规则的时候就是在控制 “没有一条路径会比其他路径长出俩倍


1)定义一个红黑树

enum Colour
{
	BLACK,
	RED
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	Colour _col;
	pair<K, V> _kv;

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

注意:

  1. 不同于AVL树,这里没有平衡因子
  2. RED,BLACK定义为enum类型
  3. 其他和AVL相似

2)红黑树的插入

插入节点的逻辑和AVL一模一样(略)

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;
	}
}
// 新增节点,颜色是红色,可能破坏规则3,产生连续红色节点
cur = new Node(kv);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
	parent->_right = cur;
	cur->_parent = parent;
}
else
{
	parent->_left = cur;
	cur->_parent = parent;
}

维持红黑树规则

插入节点为RED,父节点也为RED就需要调整了

while (parent && parent->_col == RED)

Ⅰuncle节点在grandfather节点的右边(分三种情况:)

if (parent == grandfather->_left)

①uncle节点为RED

if (uncle && uncle->_col == RED)
此时由于所有路径的黑节点都是相等的,只需要调整颜色即可,不需要旋转
在这里插入图片描述
注意:

  1. 如果grandparent节点有父节点,应继续向上判断
  2. 如果grandparent节点是根节点,要将其的_col置为BLACK(可以放在后面统一处理)

代码如下:

parent->_col = uncle->_col = BLACK;
grandfather->_col = RED; //保证所有路径一黑一红间隔(最短)
cur = grandfather;
parent = cur->_parent;
②uncle节点为BLACK或为nullptr(插入节点在parent节点的左边,右单旋)

else
比AVL控制平衡因子更简单,只需要改变旋转后颜色

在这里插入图片描述

if (cur == parent->_right)//左单旋
{
	RotateL(grandfather);//注意这里不是parent
	grandfather->_col = RED;
	parent->_col = BLACK;
}

右单旋代码参考: 数据结构—AVL树

③uncle节点为BLACK或为nullptr(插入节点在parent节点的右边,右左单旋)

同理,双旋后变色(双旋条件:grandfather,parent,cur节点呈折线)
在这里插入图片描述

RotateR(parent);
RotateL(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;

右左单旋代码参考: 数据结构—AVL树

Ⅱuncle节点在grandfather节点的左边 (同理 方向相反略)

if (parent == grandfather->_right)

3)红黑树的检测

① 是否存在两个连续的红节点

简单递归

bool CheckRED_RED(Node* cur)
{
	if (cur == nullptr)
		return true;
	if (cur->_parent && cur->_parent->_col == RED && cur->_col)
	{
		cout << cur->_kv.first << "and his father" << cur->_parent->_kv.first << "is red" << endl;
		return false;
	}
return CheckRED_RED(cur->_left)&& CheckRED_RED(cur->_right);
}

②每条路径上黑节点是否一样

设置一个基准值benchmark,找出第一条路经的黑节点数

int benchmark = 0;
Node* cur = _root;
while (cur)
{
	if (cur->_col == BLACK)
		benchmark++;
	cur = cur->_left;
}
bool judge_CheckBlackNum = true;

同时传入一个bool类型的 judge_CheckBlackNum 引用,再DFS遍历每条路径,benchmark--,走到nullptr,benchmark不为0就将 judge_CheckBlackNum 置false

void CheckBlackNum(Node* cur, bool& judge, int benchmark)
{
if (cur == nullptr)
{
	if (benchmark != 0)
	{
		judge &= false;
		cout << "wrong"<<  endl;
		return;
	}
	else
		return;
}
if (cur->_col == BLACK)
	CheckBlackNum(cur->_left, judge, benchmark - 1);
else
	CheckBlackNum(cur->_left, judge, benchmark);
/// /// ///
if (cur->_col == BLACK)
	CheckBlackNum(cur->_right ,judge, benchmark - 1);
else
	CheckBlackNum(cur->_right, judge, benchmark);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值