高阶数据结构:红黑树

一.红黑树的介绍

1.红黑树的概念

红黑树是一棵二叉搜索树,他的每个结点增加一个存储位来表示结点的颜色,可以是红色或者黑色。 通过对任何一条从根到叶子的路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路 径长出2倍,因而是接近平衡的。

2、红黑树特点

  1. 根结点为黑色;

  2. 每个结点不是黑色就是红色;

3.如果结点为红色,那么该节点的两个孩子节点为黑色,即任意一条路径上没有连续的红色节点;

  1. 对于任意一个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点。

思考⼀下,红黑树如何确保最⻓路径不超过最短路径的2倍的?

• 由规则4可知,从根到NULL结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径就就是全是黑色结点的路径,假设最短路径长度为bh(black height)。

• 由规则2和规则3可知,任意⼀条路径不会有连续的红色结点,所以极端场景下,最长的路径就是一黑⼀红间隔组成,那么最长路径的长度为2*bh。

• 综合红黑树的4点规则而言,理论上的全黑最短路径和一黑一红的最黑路径并不是在每棵红黑树都存在的。假设任意⼀条从根到NULL结点路径的长度为x,那么bh <= h <= 2*bh。(在插入节点时就可以看出来)

3.红黑树的效率

假设N是红黑树树中结点数量,h最短路径的长度,那么2^h − 1 <= N <= 2^(2∗h) − 1 , 由此推出h ≈ logN ,也就是意味着红黑树增删查改最坏也就是走最长路径 2 ∗ logN,那么时间复杂度还是 O(logN)

二.红黑树的实现

1.单节点:

1.使用pair作为存储key和value

2.三指针:_parent,_left,_right来连接构造树的结构

3.用枚举变量_col用于存储颜色

cpp 复制代码
enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{
	}
	pair<K, V>_kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;
};

2.类结构:

cpp 复制代码
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
private:
Node* _root=nullptr;
}

3.插入------insert

我们直接来对插入的节点,插入处parent,parent的兄弟:uncle,以及parent的parent:grandparent这几个维度协同进行分析。(完全基于红黑的特点)

1.对于新节点的颜色:假设新节点是黑色,那么由于插入的地方只是树枝,必然会违反所有路径上的黑色数量相同这一原则,那么选项只有:新节点只能是红色的。(留个问题那么黑色是怎么增加的)

2.对于parent的颜色:如果parent的颜色是黑色,那么可以直接插入(不会违反任何规则);如果parent的颜色是红色,那么就需要观察uncle是否存在,什么颜色:

(1)uncle存在并且为红色:那么parent,uncle一起和grandparent交换颜色,并且以grandparent的_parent为新的parent,cur=grandparent跟随新的parent然后重复分析2的对自身,uncle的观察。有三种结束的情况:

1.发现自身(parent)为黑。

2.uncle不断为红,和自己不断地向上置换,直到和_root置换(_root变成红色),结束,并且把_root重置为黑。

3.发现uncle为黑(不可能不存在),按照情况进行旋转(下面)。

情况1:

情况2:

情况3:我们假设c的位置情况不确定和p的位置不确定就是下面旋转要讨论的,图片只是一种情况(不是通用的)

(2)uncle不存在或者为黑:那么只能以自身上一个节点(grandparent)为基点旋转,各种旋转情况:

1.发现parent在grandparent的左边:

------cur在parent的左边:直接以grandparent为基点进行右旋转,并且修改颜色:grandparent为红,parent为黑。

------cur在parent的右边:先以parent为基点进行左旋转,然后以grandparent为基点进行右旋转,并且修改颜色:grandparent为红,cur为黑。

2.发现parent在grandparent的右边:

------cur在parent的右边:直接以grandparent为基点进行左旋转,并且修改颜色:grandparent为红,parent为黑。

------cur在parent的左边:先以parent为基点进行右旋转,然后以grandparent为基点进行左旋转,并且修改颜色:grandparent为红,cur为黑。

具体代码:

cpp 复制代码
bool insert(const pair<K, V>& kv)
{
	Node* newnode = new Node(kv);
	if (_root == nullptr)
	{
		_root = newnode;
		_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;
		}
	}
	if (parent->_kv.first < kv.first)
	{
		parent->_right = newnode;
		newnode->_parent = parent;
	}
	else
	{
		parent->_left = newnode;
		newnode->_parent = parent;
	}
	newnode->_col = RED;
	cur = newnode;
	while (parent && parent->_col == RED)//以parent作为循环判断
	{
		Node* grandparent = parent->_parent;
		//parent为g的左边
		if ( parent == grandparent->_left)
		{
			Node* uncle = grandparent->_right;
			//uncle存在并且为红
			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandparent->_col = RED;
				parent = grandparent->_parent;
				cur = grandparent;
			}
			else
			{
				//uncle不存在,或者为黑
				if (parent->_left == cur)//
				{
					RotateR(parent->_parent);
					parent->_col = BLACK;
					parent->_right->_col = RED;
					break;
				}
				else
				{
					RotateLR(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
			}
		}
		else
		{
			//parent为g的右边
			Node* uncle = grandparent->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandparent->_col = RED;
				parent = grandparent->_parent;
				cur = grandparent;
			}
			else
			{
				//uncle不存在,或者为黑
				if (parent->_right == cur)//
				{
					RotateL(parent->_parent);
					parent->_col = BLACK;
					parent->_left->_col = RED;
				}
				else
				{
					RotateRL(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
			}
		}
	}
	_root->_col = BLACK;
	return true;
}

用于旋转的代码:

cpp 复制代码
void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	parent->_left = subLR;
	if (subLR)
	{
		subLR->_parent = parent;
	}
	Node* parentparent = parent->_parent;
	subL->_right = parent;
	parent->_parent = subL;
	if (parentparent)
	{
		if (parentparent->_left == parent)
		{
			parentparent->_left = subL;
		}
		else
		{
			parentparent->_right = subL;
		}
	}
	else
	{
		_root = subL;
	}
	subL->_parent = parentparent;


}
//左旋
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	parent->_right = subRL;
	if (subRL)
	{
		subRL->_parent = parent;
	}
	Node* parentparent = parent->_parent;
	subR->_left = parent;
	parent->_parent = subR;
	if (parentparent)
	{
		if (parentparent->_left == parent)
		{
			parentparent->_left = subR;
		}
		else
		{
			parentparent->_right = subR;
		}
	}
	else
	{
		_root = subR;
	}
	subR->_parent = parentparent;
}
//左右旋
void RotateLR(Node* parent)
{
	
	RotateL(parent->_left);
	RotateR(parent);//subLR的因子已经更新好了
}
//右左旋
void RotateRL(Node* parent)
{
	
	RotateR(parent->_right);
	RotateL(parent);

}

4.查找------find

cpp 复制代码
Node* find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

5.前序打印

cpp 复制代码
void RBTPREorder()
{
	PREorder(_root);
}
void PREorder(Node* root)
{
	if (root == nullptr)
	{
		return;
	}
	cout << root->_kv.first << root->_col << " ";
	//cout << endl;
	PREorder(root->_left);
	PREorder(root->_right);
}

6.检查

cpp 复制代码
bool Check(Node* root, int BLACKnum, int refnum)
{
	if (root == nullptr)
	{
		if (BLACKnum != refnum)
		{
			cout << "BLACK数量不相同" << endl;
			return false;
		}
		return true;
	}
	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << "红色连续" << endl;
		return false;
	}
	if (root->_col == BLACK)
	{
		BLACKnum++;
	}
	return Check(root->_left, BLACKnum, refnum) && Check(root->_right, BLACKnum, refnum);
}
bool Is_RBTree()
{
	if (_root == nullptr)
	{
		return true;
	}
	if (_root->_col == RED)
	{
		return false;
	}
	int refnum = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			refnum++;
		}
		cur = cur->_left;
	}
	int BLACKnum=0;
	return Check(_root, BLACKnum, refnum);
}
相关推荐
噜啦噜啦嘞好2 小时前
算法篇:二分查找
数据结构·c++·算法·leetcode
setmoon2142 小时前
C++中的构建器模式
开发语言·c++·算法
2301_815482932 小时前
C++中的桥接模式变体
开发语言·c++·算法
yunyun321232 小时前
C++与量子计算模拟
开发语言·c++·算法
暮冬-  Gentle°2 小时前
移动设备上的C++优化
开发语言·c++·算法
2401_874732532 小时前
C++中的装饰器模式高级应用
开发语言·c++·算法
m0_662577972 小时前
模板编译期哈希计算
开发语言·c++·算法
m0_662577972 小时前
C++代码静态检测
开发语言·c++·算法
阿贵---2 小时前
编译器命令选项优化
开发语言·c++·算法