【C++】红黑树

06-02 897阅读

1.红黑树的概念

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

2.红黑树的性质

【C++】红黑树

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点必须是黑色的 ,任何路径没有连续的红色节点
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,每条路径上黑色节点数量相等
  5. 每个NIL叶子结点都是黑色的

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

【C++】红黑树

通过该图结合性质3,4就能满足

3.红黑树的实现

3.1节点的定义

enum Colur
{
	RED,
	BLACK
};
template
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	pair _kv;
	Colur _col;
	BLTreeNode(const pair& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{ }
};

与AVL树相比,将平衡因子替换成颜色。

为什么默认将新插入节点颜色给成红色?

因为给黑色导致该路径黑色节点增加,红黑树性质每条路径上黑色节点数量相同,这样会导致所有路径节点发生变化。而给红色,当前路径违反了不能有两个连续红色节点性质,进行局部颜色调整和旋转即可。

3.2插入操作

  1. 按照二叉搜索的树规则插入新节点
  2. 检测新节点插入后,红黑树的性质是否造到破坏

    约定cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

  • 情况一:cur为红,p为红,g为黑,u存在且为红

    【C++】红黑树

    【C++】红黑树

    这是一般规律下的抽象图,下面用一个特例图来帮助我们更好理解情况1的实现过程

    【C++】红黑树

    • 情况二:cur为红,p为红,g为黑,u不存在/u存在且为黑

      【C++】红黑树

      总结:

      红黑树插入关键看uncle

      1.uncle存在且为红,变色+继续往上更新

      2.uncle不存在,uncle存在且为黑,旋转+变色

      bool Insert(const pair& kv)
      {
      	if (_root == nullptr)
      	{
      		_root = new Node(kv);
      		_root->_col = BLACK;
      		return true;
      	}
      	//查找插入位置
      	Node* cur = _root;
      	Node* parent = nullptr;
      	while (cur)
      	{
      		if (cur->_kv.first _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->_left = cur;
      	}
      	else
      	{
      		parent->_right = cur;
      	}
      	cur->_parent = parent;
      	while (parent&&parent->_col==RED)
      	{
      		Node* grandfather = parent->_parent;
      		if (parent == grandfather->_left)
      		{
      			Node* uncle = grandfather->_right;
      			//uncle存在且为红
      			if (uncle && uncle->_col == RED)
      			{
      				//变色
      				parent->_col = uncle->_col = BLACK;
      				grandfather->_col = RED;
      				//更新节点.继续向上处理
      				cur= grandfather;//注意赋值的顺序
      				parent = grandfather->_parent;
      			}
      			else//uncle不存在或为黑
      			{//判断是单旋还是双旋
      				if (cur == parent->_left)
      				{
      					//	  g
      					//	p
      					//c
      					RotateR(grandfather);
      					parent->_col = BLACK;
      					grandfather->_col = RED;
      				 }
      				else
      				{
      					//    g
      					//  p
      					//	  c
      					RotateL(parent);
      					RotateR(grandfather);
      					cur->_col = BLACK;
      					grandfather->_col = RED;
      				}
      				break;
      			}
      		}
      		else//parent == grandfather->_right
      		{
      			Node* uncle = grandfather->_left;
      			//uncle存在且为红
      			if (uncle && uncle->_col == RED)
      			{
      				parent->_col = uncle->_col = BLACK;
      				grandfather->_col = RED;
      				//更新节点.继续向上处理
      				cur= grandfather;
      				parent = grandfather->_parent;
      			}
      			else//uncle不存在或为黑
      			{//判断是单旋还是双旋
      				if (cur == parent->_right)
      				{
      					//g  
      					//  p
      					//    c
      					RotateL(grandfather);
      					parent->_col = BLACK;
      					grandfather->_col = RED;
      				}
      				else
      				{
      					//g
      					//  p
      					//c
      					RotateR(parent);
      					RotateL(grandfather);
      					cur->_col = BLACK;
      					grandfather->_col = RED;
      				}
      				break;
      			}
      		}
      	}
      	_root->_col = BLACK;
      	return true;
      }
      

      代码思路:

      1.查找插入位置,创建节点并插入,进行平衡调整。

      2.平衡调整中循环成立条件为:父节点存在且颜色为红。因为新插入节点为红,父节点也为红,意味着存在两个连续的红色节点,违反红黑树性质,此时需要通过变色和旋转来修复树的性质。

      3.在循环中确定祖父节点和叔叔节点。通过判断叔叔节点来进行操作,如上文总结。

      4.当父节点不存在或为黑色时调整结束。

      3.3验证

      1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

      将红黑树按中序遍历的方式存入vector中,通过前后比较元素看是否满足升序

      bool IsBST() {
      	vector result;
      	_Inorder(_root,result);
      	for (int i = 1; i _kv.first);
      	_Inorder(root->_right, result);
      }
      

      2.检测是否满足红黑树性质

      • 检查颜色
        bool CheckColur(Node* root, int blacknum, int benchmark)
        {//走到null后判断黑色节点数量与基准值是否相同
        	if (root == nullptr)
        	{//判断是否违反每条路径中黑色节点数量必须相同
        		if (blacknum != benchmark)
        		{
        			return false;
        		}
        		return true;
        	}
        	
        	//判断树中黑节点数量
        	if (root->_col==BLACK)
        	{
        		blacknum++;
        	}
        	//判断树中是否有连续红节点情况
        	if (root->_col == RED && root->_parent && root->_parent->_col == RED)
        	{
        		cout _kv.first _right, blacknum, benchmark);
        }
        

        注意:

        这里第二个参数blacknum不传引用刚刚好,这样可以正确的递归计算每一条路径黑色节点数量,返回时由于不是传引用,blacknum作为临时变量随着栈帧的销毁而销毁,不会影响到另一条路径黑色节点数量的计算

        //封装接口,提供外界使用
        bool IsBalance()
        {
        	return IsBalance(_root);
        }
        bool IsBalance(Node* root)
        {
        	if (root == nullptr)
        	{
        		return true;
        	}
        	if (root->_col != BLACK)
        	{
        		return false;
        	}
        	//计算基准值,随便计算一条路径黑节点的数量
        	int benchmark = 0;
        	Node* cur = root;
        	while (cur)
        	{
        		if(cur->_col==BLACK)
        		benchmark++;
        		//选择一条路径来计算黑节点数量
        		cur = cur->_left;
        	}
        	return CheckColur(root, 0, benchmark);
        }
        

        3.4红黑树与AVL树的比较

        都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2​N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

        4.整体代码

        • RBTree.h
          #pragma once
          #include
          #include
          using namespace std;
          enum Colur
          {
          	RED,
          	BLACK
          };
          template
          struct RBTreeNode
          {
          	RBTreeNode* _left;
          	RBTreeNode* _right;
          	RBTreeNode* _parent;
          	pair _kv;
          	Colur _col;
          	RBTreeNode(const pair& kv)
          		:_left(nullptr)
          		, _right(nullptr)
          		, _parent(nullptr)
          		,_kv(kv)
          		,_col(RED)
          	{ }
          };
          template
          struct RBTree
          {
          	typedef RBTreeNode Node;
          public:
          	bool Insert(const pair& kv)
          	{
          		if (_root == nullptr)
          		{
          			_root = new Node(kv);
          			_root->_col = BLACK;
          			return true;
          		}
          		//查找插入位置
          		Node* cur = _root;
          		Node* parent = nullptr;
          		while (cur)
          		{
          			if (cur->_kv.first _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->_left = cur;
          		}
          		else
          		{
          			parent->_right = cur;
          		}
          		cur->_parent = parent;
          		while (parent&&parent->_col==RED)
          		{
          			Node* grandfather = parent->_parent;
          			if (parent == grandfather->_left)
          			{
          				Node* uncle = grandfather->_right;
          				//uncle存在且为红
          				if (uncle && uncle->_col == RED)
          				{
          					//变色
          					parent->_col = uncle->_col = BLACK;
          					grandfather->_col = RED;
          					//更新节点.继续向上处理
          					cur= grandfather;//注意赋值的顺序
          					parent = grandfather->_parent;
          				}
          				else//uncle不存在或为黑
          				{//判断是单旋还是双旋
          					if (cur == parent->_left)
          					{
          						//	  g
          						//	p
          						//c
          						RotateR(grandfather);
          						parent->_col = BLACK;
          						grandfather->_col = RED;
          					 }
          					else
          					{
          						//    g
          						//  p
          						//	  c
          						RotateL(parent);
          						RotateR(grandfather);
          						cur->_col = BLACK;
          						grandfather->_col = RED;
          					}
          					break;
          				}
          			}
          			else//parent == grandfather->_right
          			{
          				Node* uncle = grandfather->_left;
          				//uncle存在且为红
          				if (uncle && uncle->_col == RED)
          				{
          					parent->_col = uncle->_col = BLACK;
          					grandfather->_col = RED;
          					//更新节点.继续向上处理
          					cur= grandfather;
          					parent = grandfather->_parent;
          				}
          				else//uncle不存在或为黑
          				{//判断是单旋还是双旋
          					if (cur == parent->_right)
          					{
          						//g  
          						//  p
          						//    c
          						RotateL(grandfather);
          						parent->_col = BLACK;
          						grandfather->_col = RED;
          					}
          					else
          					{
          						//g
          						//  p
          						//c
          						RotateR(parent);
          						RotateL(grandfather);
          						cur->_col = BLACK;
          						grandfather->_col = RED;
          					}
          					break;
          				}
          			}
          		}
          		_root->_col = BLACK;
          		return true;
          	}
          	void RotateL(Node* parent)
          	{
          		_rotateCount++;
          		Node* cur = parent->_right;
          		Node* curleft = cur->_left;
          		Node* ppnode = parent->_parent;
          		//第一次改变链接
          		parent->_right = curleft;
          		if (curleft)
          		{
          			curleft->_parent = parent;
          		}
          		//第二次改变链接
          		cur->_left = parent;
          		parent->_parent = cur;
          		//判断根节点的链接情况
          		//为根节点调整平衡因子情况
          		if (parent == _root)
          		{
          			_root = cur;
          			cur->_parent = nullptr;
          		}
          		//树中的部分调整情况
          		else
          		{
          			if (ppnode->_left == parent)
          			{
          				ppnode->_left = cur;
          			}
          			else
          			{
          				ppnode->_right = cur;
          			}
          			cur->_parent = ppnode;
          		}
          	}
          	void RotateR(Node* parent)
          	{
          		_rotateCount++;
          		Node* cur = parent->_left;
          		Node* curright = cur->_right;
          		Node* ppnode = parent->_parent;
          		//第一次链接
          		parent->_left = curright;
          		if (curright)
          		{
          			curright->_parent = parent;
          		}
          		//第二次链接
          		cur->_right = parent;
          		parent->_parent = cur;
          		//调整根节点链接关系
          		if (parent == _root)
          		{
          			_root = cur;
          			cur->_parent = nullptr;
          		}
          		else
          		{
          			if (ppnode->_left == parent)
          			{
          				ppnode->_left = cur;
          			}
          			else
          			{
          				ppnode->_right = cur;
          			}
          			cur->_parent = ppnode;
          		}
          	}
          	bool CheckColur(Node* root, int blacknum, int benchmark)
          	{
          		if (root == nullptr)
          		{
          			if (blacknum != benchmark)
          			{
          				return false;
          			}
          			return true;
          		}
          		
          		//判断树中黑节点数量
          		if (root->_col==BLACK)
          		{
          			blacknum++;
          		}
          		//判断树中是否有连续红节点情况
          		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
          		{
          			cout _kv.first _right, blacknum, benchmark);
          	}
          	bool IsBalance()
          	{
          		return IsBalance(_root);
          	}
          	bool IsBalance(Node* root)
          	{
          		if (root == nullptr)
          		{
          			return true;
          		}
          		if (root->_col != BLACK)
          		{
          			return false;
          		}
          		//计算基准值
          		int benchmark = 0;
          		Node* cur = root;
          		while (cur)
          		{
          			if(cur->_col==BLACK)
          			benchmark++;
          			//选择一条路径来计算黑节点数量
          			cur = cur->_left;
          		}
          		return CheckColur(root, 0, benchmark);
          	}
          	int Height()
          	{
          		return Height(_root);
          	}
          	
          	int Height(Node* root)
          	{
          		if (root == nullptr)
          		{
          			return 0;
          		}
          		int leftheight = Height(root->_left);
          		int rightheight = Height(root->_right);
          		return leftheight > rightheight ?
          			leftheight + 1 : rightheight + 1;
          	}
          	bool IsBST() {
          		vector result;
          		_Inorder(_root,result);
          		for (int i = 1; i _kv.first);
          		_Inorder(root->_right, result);
          	}
          	public:
          		int _rotateCount = 0;
          	private:
          		Node* _root = nullptr;
          };
          
          • 测试代码
            #include"AVLTree.h"
            #include"RBTree.h"
            #include
            //int main()
            //{
            //	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
            //	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
            //	RBTree t;
            //	for (auto e : a)
            //	{
            //		t.Insert(make_pair(e, e));
            //		cout 
            	const int N = 1000000;
            	vector
            		v.push_back(i);
            	}
            	AVLTree
            		avlt.Insert(make_pair(e, e));
            	}
            	cout 
            		rbt.Insert(make_pair(e, e));
            	}
            	cout 
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码