【C++】红黑树

欲穷千里目,更上一层楼。

前言

这是我自己学习C++的第十六篇博客总结。后期我会继续把C++学习笔记开源至博客上。

上一期笔记是关于C++的AVL树知识,没看的同学可以过去看看:

【C++】AVL树-CSDN博客https://blog.csdn.net/hsy1603914691/article/details/147250482

红黑树

红黑树的概念

  1. 红黑树是一种二叉搜索树。在红黑树中,每个节点除了保存键值之外 ,还增加了一个额外的颜色属性 ,颜色可以是红色或黑色

  2. 通过对从根节点到叶子节点的每条路径上的节点颜色进行约束,红黑树保证了任意一条路径的长度都不会超过其他路径长度的两倍 ,因此红黑树是接近平衡的

红黑树的规则

  1. 每个结点不是红色就是黑色
  2. 根结点是黑色
  3. 如果一个结点是红色的,则它的两个孩子结点必须是黑色的
  4. 对于任意一个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点

红黑树的效率

  1. 红黑树 的查找效率为 O(logN)
  2. AVL树 通过 高度差 直观的控制了平衡; 红黑树 通过 4条规则的颜色约束 ,间接的实现了近似平衡。 它们效率都是同一档次 ,但 是相对而言,插 入相同数量的结点, 红黑树的旋转次数是更少的
cpp 复制代码
#include <iostream>
using namespace std;

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	typedef RBTreeNode<K, V> Node;
	K _key;
	V _val;
	Node* _left;
	Node* _right;
	Node* _parent;
	Colour _col;
	RBTreeNode(const K& key, const V& val)
		: _key(key)
		, _val(val)
		, _col(RED)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
	typedef RBTree<K, V> Tree;
public:

	RBTree()
		:_root(nullptr)
	{}
	bool insert(const K& x, const V& y)
	{
		if (_root == nullptr)//如果插入的节点为根节点
		{
			_root = new Node(x, y);
			_root->_col = BLACK;
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;
		while (cur != nullptr)//找到新节点的插入位置
		{
			if (cur->_key < x)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > x)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;//不允许有重复
			}
		}

		cur = new Node(x, y);
		if (parent->_key < cur->_key)//插入新节点
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		while (parent&&parent->_col==RED)//父节点为红色
		{
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)//父节点在左侧
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)//叔叔节点存在,且为红色
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = grandfather->_parent;
				}
				else//叔叔节点不存在;叔叔节点存在,且为黑色
				{
					if (cur == parent->_left)//插入节点在父节点左侧
					{
						Rotate_Right(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else//插入节点在父节点右侧
					{
						Rotate_Left(parent);
						Rotate_Right(grandfather);
						grandfather->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = grandfather->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						Rotate_Left(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						Rotate_Right(parent);
						Rotate_Left(grandfather);
						grandfather->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}
			}
		}
		return true;
	}

	void InOrder()
	{
		_InOrder(_root);
	}

	Node* Find(const K& x)
	{
		Node* cur = _root;
		while (cur != nullptr)
		{
			if (cur->_key < x)
			{
				cur = cur->_right;
			}
			else if (cur->_key > x)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool Check(Node* root, int blackNum, const int refNum)
	{
		if (root == nullptr)
		{
			// 前序遍历走到空时,意味着一条路径走完了
			if (refNum != blackNum)
			{
				cout << "存在黑色结点的数量不相等的路径" << endl;
				return false;
			}
			return true;
		}

		// 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "存在连续的红色结点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, blackNum, refNum)
			&& Check(root->_right, blackNum, refNum);
	}

	bool IsBalance()
	{
		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;
		}

		return Check(_root, 0, refNum);
	}
private:
	Node* _root;

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		else
		{
			_InOrder(root->_left);
			cout << root->_key << " " << root->_val << endl;
			_InOrder(root->_right);
		}
	}

	void Rotate_Right(Node* parent)
	{
		Node* ppNode = parent->_parent;
		Node* sub_left = parent->_left;
		Node* subl_right = sub_left->_right;

		sub_left->_right = parent;
		parent->_parent = sub_left;
		parent->_left = subl_right;
		if (subl_right)
		{
			subl_right->_parent = parent;
		}
		if (ppNode == nullptr)
		{
			_root = sub_left;
			sub_left->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = sub_left;
			}
			else
			{
				ppNode->_right = sub_left;
			}
		}
	}

	void Rotate_Left(Node* parent)
	{
		Node* ppNode = parent->_parent;
		Node* sub_right = parent->_right;
		Node* subr_left = sub_right->_left;

		sub_right->_left = parent;
		parent->_parent = sub_right;
		parent->_right = subr_left;
		if (subr_left)
		{
			subr_left->_parent = parent;
		}
		if (ppNode == nullptr)
		{
			_root = sub_right;
			sub_right->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = sub_right;
			}
			else
			{
				ppNode->_right = sub_right;
			}
		}
	}

};
int main()
{
	RBTree<int, int> rbt;
	rbt.insert(999, 1);
	rbt.insert(998, 2);
	rbt.insert(997, 3);
	rbt.insert(996, 4);
	rbt.insert(995, 5);
	rbt.insert(994, 6);
	rbt.insert(993, 7);
	rbt.insert(992, 8);
	rbt.insert(991, 9);
	rbt.insert(990, 10);
	rbt.InOrder();
	return 0;
}

在这里获取最长路径和最短路径,检查最长路径不超过最短路径的2倍是不可行的,因为即使满足这个条件,红黑树也可能颜色不满足规则。当前看起来没有问题,但在后续继续插入节点时可能会出现问题。因此,我们应当检查四点规则,满足这四点规则能够保证最长路径不超过最短路径的2倍。

  1. 规则1:枚举颜色类型,这样实现可以保证颜色不是黑色就是红色。
  2. 规则2:直接检查根节点即可。
  3. 规则3:通过前序遍历检查,当遇到红色节点时查找孩子节点不太方便,因为孩子节点有两个,且不一定存在。相反,检查父节点的颜色更为方便。
  4. 规则4:在前序遍历中,使用形参记录从根节点到当前节点的黑色节点数量。遍历过程中,遇到黑色节点就增加黑色节点数量,遍历到空节点即可计算出一条路径的黑色节点数量。将任意一条路径的黑色节点数量作为参考值,依次比较即可。
相关推荐
不会c嘎嘎6 小时前
数据结构--顺序表
数据结构
1白天的黑夜17 小时前
数据结构之堆(topk问题、堆排序)
c语言·数据结构·算法
L下自成蹊L8 小时前
2. 数据结构基本概念 (2)
数据结构·算法·面试·数据结构与算法基础
肥猪猪爸16 小时前
使用LSTM进行时间序列分析
数据结构·人工智能·rnn·深度学习·算法·lstm·时间序列分析
2401_8370885017 小时前
List<Integer> list=new ArrayList<>()
数据结构·list
<但凡.18 小时前
C++修炼:map和set的封装
数据结构·c++
生活很暖很治愈18 小时前
《函数栈帧的创建和销毁》
c语言·数据结构·c++·编辑器
yours_Gabriel18 小时前
【力扣】面试题 01.04. 回文排列
java·数据结构·leetcode
Musennn18 小时前
leetcode106.从中序与后序遍历序列构造二叉树:索引定位与递归分治的完美配合
java·数据结构·算法·leetcode