map和set的底层结构——红黑树

1.红黑树的概念

红黑树是一种二叉搜索树,在每个节点上加一个储存位表示颜色的概念,只有红黑两种颜色,通过对节点颜色的限制,使得红黑树的最长路径不超过最短路径的二倍,因此是接近平衡的。

2.红黑树的性质

1.每个节点只有红和黑两种颜色

2.根节点是黑色的

3.如果一个节点是红色的,则他的两个孩子节点是黑色的

4.对于每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数目的黑色节点

5.每个叶子节点都是黑色的(此处的叶子节点指的是空节点)

3.红黑树节点的定义

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)
	{}

};

红黑树多了储存颜色的功能;而且红黑树插入节点是默认插入红色的节点,因为如果插入黑色节点,必定破坏规则4,而插入红节点,可能破坏规则三,可能出现两个连续的红色节点,但是可以修改,也有可能插入红色节点,刚好满足条件,所以我们选择插入默认是红色的节点;

4.红黑树的插入操作

红黑树的插入大致分为两种情况:

情况一:叔叔节点存在,且为红色

这时候,只需要变色就可以了

p和u节点变成黑色,g节点变成红色,然后继续向上更新,直到根节点

情况二:叔叔节点不存在,或者叔叔节点存在但是为黑色

这个时候需要变色+旋转

我们还需要考虑cur是在parent的左边还是右边,旋转的方向不同

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;

	//调整颜色
	//parent不存在,或者parent颜色为黑,跳出循环
	while (parent && parent->_col == RED)
	{
		//关键看叔叔
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;
			//叔叔存在且为红
			// 变色即可
			if (uncle && uncle->_col==RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续向上更新
				cur = grandfather;
				parent = cur->_parent;
			}
			else //叔叔不存在或者叔叔为黑
			{
				if (cur == parent->_left)
				{
					//右旋
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//左右旋
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		else
		{
			Node* uncle = grandfather->_left;
			//叔叔存在且为红,变色即可
			if (uncle&& uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续向上更新
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				//叔叔不存在,或者为黑
				if (cur == parent->_right)
				{
					//左旋
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;

				}
				else
				{
					//右左旋
					RotateR(parent);
					RotateL(grandfather);
					cur->_col=BLACK;
					grandfather->_col = RED;

				}
				break;
			}
		}
	}
	_root->_col = BLACK;
	return true;
}

5.红黑树的验证

如何判断红黑树是否平衡呢?

三个方面:根节点是否为黑色,每条路径的黑色节点数量是否相同 是否存在连续的红色节点

bool IsBalance()
{
	if (_root->_col == RED)
	{
		return false;
	}

	int refNum = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			++refNum;
		}
		cur = cur->_left;
	}
	return check(_root,0,refNum);
}

bool check(Node* root,int blackNum,const int refNum)
{
	if (root == nullptr)
	{
		if (blackNum != refNum)
		{
			cout << "存在黑色节点的数量不相等的路径" << "\n";
			return false;
		}
		return true;
	}
	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << root->_kv.first << "存在连续的红色节点" << "\n";
		return false;
	}
	if (root->_col == BLACK)
	{
		++blackNum;
	}
	return check(root->_left, blackNum, refNum) && check(root->_right, blackNum, refNum);

}

6.红黑树和AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,时间复杂度都是O(logN),红黑树不追求绝对平衡,只需保持最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能更好,所以实际运用中红黑树用的更多。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值