飞道的博客

红黑树(RBtree)

224人阅读  评论(0)

tips:这篇需要用到AVLtree的部分知识!
本人之前写的AVLtree, 不妨点击看下或阅读其他大佬的文章!谢谢~~
https://blog.csdn.net/weixin_44024891/article/details/104910691

红黑树(RBtree)是一个相对平衡的二叉搜索树。

为什么是相对呢?
      相对于二叉搜索树, 他很平衡, 但对于高度平衡的AVLtree而言,他就显得不那么平衡了。

这块的概念很重要!我们知道二叉搜索树由于它的不平衡, 不规范的插入会导致树的性能下降,在极端情况下还不如链表。 而AVLtree由于过度平衡, 导致插入节点时为了调整平衡因子会花很大的代价,使得性能下降。

而我们RBtree介于两者中间, 拥有双方的优点,有着很好的性能。

那红黑树(RBtree)该如何实现呢?
      在设计的时候,我们抛去AVLtree平衡因子的概念, 添加红色、黑色标识, 每个结点要么是黑色、要么是红色。

并且还要满足以下几点要求:
      1)、一个结点要么为黑色,要么为红色。
      2)、根结点的颜色为黑色
      3)、如果一个结点为红色, 则它的两个孩子要为黑色。
      4)、对于每一个结点(任意一个),该结点到其所有后代叶子结点路径上, 均包含相同的黑色结点个数。 (重点)
      5)、对于每一个结点(任意一个),它的所有路径中最长的路径长度不超过最短路径的2倍。(重点)
      6)、每个叶子结点都是黑色的,(此处的叶子结点指的是空结点)(这样定义叶子结点, 是为了区分路径!)


只要满足以上的要求, 就可以实现一个红黑树。并且查找效率很高。
每个条件之间互相约束, 在不满足条件时,需要调整。最终会得到一个高度平衡的树(小于AVLtree)。由于允许最长路径的长度不超过最短路径的2倍, 也给了很大的空间不需要做调整(AVLtree不超过1)。两者的结合得到高效率的RBtree。

那么如何做调整呢?
      我这块以插入接口为例。
在找到合适的插入位置之后, 对其判断是否影响到RBtree的满足条件。

插入后大体分为4类情况:

1)、插入结点的父结点为黑色
      答:如果父结点为黑色, 我们不需要做调整, 属于正常情况。

2)、插入结点父结点为红色、且叔叔结点存在且为红色(叔叔结点是父亲结点的兄弟结点)
      答:将父亲结点和叔叔结点置为黑色。 祖父结点置为红色(祖父结点为父亲结点的父亲), 再以祖父结点为新的起点, 接着往上更新。 (如果祖父结点为根节点, 则停止更新, 并将祖父结点置为黑色)。

3)、插入结点父结点为红色, 叔叔结点存在且为黑色 或 叔叔结点不存在

a、插入的结点和父结点在一边
            答:单旋; 以父结点为轴单旋。单旋完毕之后,parent变为黑,gfather变为红,停止调整!(看图)

右旋情况


左旋情况就不列举了, cur和parent结点在gfather的右边,则左旋!其他处理和右旋一样。

b、插入的结点和父结点不在一边
            答:双旋; 以父结点为轴单旋一次, 再以祖父结点单旋一次。 cur结点变为黑色, gfather变为红色。(看图)

左右双旋

右左双旋就不介绍了, parent在gfather的右边,cur在parent的左边!其他处理和左右双旋一样!

以上就是插入的全部情况了,代码如下:

为了模仿关联式容器底层实现, 这块存储的元素是键值对(也就是pair类), 代码中提供insert(插入)接口,但未实现erase(删除)接口!

/*
	tips: _header是头结点, 不是根结点
		  _header->_parent = 根结点; 根结点->_parent = _header;
		  _header->_left = 树的最左结点
		  _header->_right = 树的最右结点
		  
	这样设计是为了方便模拟关联式容器中迭代器的实现!
*/
#include<iostream>
#include<utility>		//提供pair类

using namespace std;

//定义枚举常量 - 标志BLACK(黑色)、 RED(红色)
enum COLOR {
	BLACK,
	RED
};

//封装RBNtree树结点
template<class K, class V>
struct RBNode {
	//定义RB树的节点信息

	RBNode<K, V>* _left;
	RBNode<K, V>* _right;
	RBNode<K, V>* _parent;
	pair<K, V> _value;
	COLOR _color;		//定义一个枚举变量

	RBNode(const pair<K, V>& value = pair<K, V>())	//相当传递一个匿名的对象
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _value(value)	//pair类中自动赋值
		, _color(RED) 
	{

	}
};

//RBtree类实现
template<class K, class V>
class RBTree {
public:
	typedef RBNode<K, V> Node;
	typedef Node* pNode;

	RBTree(){
		//无参构造 - 空树
		_header = new Node;
		_header->_left = _header;
		_header->_right = _header;
		_header->_parent = nullptr;
	}
	
	//左旋接口
	void RotateL(pNode& parent) {
		
		pNode SubR = parent->_right;
		pNode SubRL = SubR->_left;
		
		SubR->_left = parent;
		parent->_right = SubRL;
		pNode pp = parent->_parent;
		if (pp != _header) {
			if (pp->_left == parent) {
				pp->_left = SubR;
			}
			else {
				pp->_right = SubR;
			}
		}
		else {
			_header->_parent = SubR;
		}

		SubR->_parent = pp;
		if (SubRL) {
			SubRL->_parent = parent;
		}
		parent->_parent = SubR;
	}
	
	//右旋接口
	void RotateR(pNode& parent) {

		pNode SubL = parent->_left;
		pNode SubLR = SubL->_right;

		SubL->_right = parent;
		parent->_left = SubLR;
		pNode pp = parent->_parent;
		if (pp != _header) {
			if (pp->_left == parent) {
				pp->_left = SubL;
			}
			else {
				pp->_right = SubL;
			}
		}
		else {
			_header->_parent = SubL;
		}

		SubL->_parent = pp;
		if (SubLR) {
			SubLR->_parent = parent;
		}
		parent->_parent = SubL;
	}
	
	//插入接口
	bool insert(const pair<K, V>& value) {

		//判断是否为空树
		if (_header->_parent == nullptr) {
			//创建根节点
			pNode root = new Node(value);
			root->_color = BLACK;	//更改根节点颜色为黑色

			//根的父节点 == 头, 头节点的父节点 == 根
			root->_parent = _header;
			_header->_parent = root;

			//头节点的左右节点==根
			_header->_left = root;
			_header->_right = root;

			return true;
		}

		//从根节点开始查找合适位置
		pNode cur = _header->_parent;
		pNode parent = nullptr;
		while (cur) {
			parent = cur;
			if (cur->_value.first == value.first)
				return false;
			else if (cur->_value.first > value.first)
				cur = cur->_left;
			else
				cur = cur->_right;
		}

		cur = new Node(value);
		if (parent->_value.first > cur->_value.first)
			parent->_left = cur;
		else
			parent->_right = cur;	
		cur->_parent = parent;

		//调整阶段
		while (cur != _header->_parent && cur->_parent->_color == RED) {
			//父亲的节点为红色(红红需要调整,) && 可以判断parent不为根 - 祖父就会存在
			
			parent = cur->_parent;
			pNode gfather = parent->_parent;
			if (gfather->_left == parent) {
				//叔叔节点在右边
				pNode uncle = gfather->_right;
				if (uncle && uncle->_color == RED) {
					//叔叔节点存在, 并且为红
					//调整方式1
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;

					//继续向上更新
					cur = gfather;
				}
				else {
					//叔叔节点存在, 但为黑 or 叔叔不存在
					//调整完毕之后对gfather往上的节点无影响, 停止调整

					//双旋情况 cur和parent不在同一遍
					if (cur == parent->_right) {
						RotateL(parent);
						swap(parent, cur);
					}
					//右旋	
					RotateR(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					//退出
					break;
				}
			}
			else if (gfather->_right == parent) {
				//叔叔节点在左边
				pNode uncle = gfather->_left;
				if (uncle && uncle->_color == RED) {
					//叔叔节点存在, 并且为红
					//调整方式1
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;

					//持续往上更新
					cur = gfather;
				}
				else {
					//叔叔节点存在, 但为黑 or 叔叔不存在
					//调整完毕之后对gfather往上的节点无影响, 停止调整

					//双旋 - parent和cur不在一边
					if (cur == parent->_left) {
						RotateR(parent);
						swap(cur, parent);
					}

					//左旋
					RotateL(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					//退出
					break;
				}
			}
		}

		//根节点强转为黑色
		_header->_parent->_color = BLACK;

		//更新, 更新最左节点和最右节点 - 方便迭代器的设计
		_header->_left = LeftMost();
		_header->_right = RightMost();

		return true;
	}

	//寻找最左节点
	pNode LeftMost() {
		pNode cur = _header->_parent;
		if (!cur) {
			//无根节点
			return cur;
		}

		while (cur->_left != nullptr) {
			cur = cur->_left;
		}
		return cur;
	}
	//寻找最右节点
	pNode RightMost() {
		pNode cur = _header->_parent;
		if (!cur) {
			return cur;
		}

		while (cur->_right != nullptr) {
			cur = cur->_right;
		}
		return cur;
	}
	
	//中序遍历
	void InOrder() {
		_InOrder(_header->_parent);
		cout << endl;
	}

private: 
	void _InOrder(const pNode& root) {
		if (root) {
			_InOrder(root->_left);
			cout << '<' << root->_value.first << "-->" << root->_value.second << '>' << endl;
			_InOrder(root->_right);
		}
	}

private:
	pNode _header;		//定义头部结点, 不是根节点。
};

void test_RBtree() {
	RBTree<int, int> rb;
	rb.insert(make_pair(4, 7));
	rb.insert(make_pair(2, 7));	
	rb.insert(make_pair(1, 7));
	rb.insert(make_pair(10, 7));
	rb.insert(make_pair(9, 7));
	rb.insert(make_pair(7, 7));

	rb.InOrder();
}

int main() {
	test_RBtree();
	return 0;
}

测试结果:

分析:
1、RBtree相对于AVLtree, 插入比较简单,且效率比较高,调整次数少。
2、在STL关联式容器中map、set、multimap、multiset容器都是采用RBtree作为底层的数据结构。学习RBtree也是对容器学习提供帮助!
3、学习RBtree个人感觉是可以替换AVLtree的。


转载:https://blog.csdn.net/weixin_44024891/article/details/105442405
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场