红黑树模拟实现

个人主页:Lei宝啊 

愿所有美好如期而遇


红黑树概念

红黑树是一种二叉搜索树,对比于AVL树,他不存储平衡因子,取而代之的是存储节点颜色,黑色或者红色,我们这里对颜色的定义使用枚举,通过对每个节点颜色的限制,使得红黑树确保没有一条路径比其他路径长两倍,因而是接近平衡的。

红黑树性质

  1. 每个节点的颜色不是黑色就是红色。
  2. 根节点必须是黑色。
  3. 如果一个节点的颜色是红色,那么它的两个孩子是黑色,不能有连续的红色节点,但是可以有连续的黑色节点。
  4. 每条路径的黑色节点的数量相等。

保证了上面几条核心性质,就能保证黑树没有一条路径比其他路径长两倍。

红黑树节点定义

enum Color
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	struct RBTreeNode* _left;
	struct RBTreeNode* _right;
	struct RBTreeNode* _parent;
	T _data;
	int _color;

	RBTreeNode(const K& data)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _color(RED)
		, _data(data)
	{}
};

我们这里将每个新节点默认的颜色设置为红色,因为如果我们默认设置成黑色,插入一个节点,那么就不是每条路径上的黑色节点数都相同了,非常不好控制,而红色节点只会影响当前路径有连续红色节点,我们后续可以通过其他手段控制。

红黑树类的结构

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

	bool Insert(const K& key, const T& val);
	bool IsBlance();
	void Inorder();

private:

	bool _IsBlance(Node *root, int real_black_num, int standard_black_num);
	void _Inorder(Node* root);

	//左单旋
	void RotateL(Node* par)	
	//右单旋
	void RotateR(Node* par)
	
	Node* _root = nullptr;
};

红黑树节点插入操作

红黑树是在二叉搜索树基础上加上限制平衡的手段,因此红黑树节点的插入可以分为两步:

第一步:

按照二叉搜索树的规则插入节点:(这里可以参考:二叉搜索树AVL树

简单来说就是节点的左子树的每个节点的值都小于节点的值,节点的右子树的每个节点的值都大于节点的值,同时对二叉搜索树进行中序遍历可以得到有序的一个序列。

		Node* newnode = new Node(data);

		if (_root == nullptr)
		{
			_root = newnode;
			_root->_color = BLACK;
			return true;
		}

		Node* cur = _root;
		Node* par = nullptr;
		while (cur)
		{
			par = cur;

			if (compare(cur->_data) == data) return false;
			else if (compare(cur->_data) < data) cur = cur->_right;
			else cur = cur->_left;
		}

		if (compare(par->_data) > data) par->_left = newnode;
		else par->_right = newnode;

		newnode->_parent = par;

		cur = newnode;

第二步:

检测新节点插入后,红黑树的性质是否遭到破坏。

因为我们设置了新节点的颜色为红色,如果这个新节点插入的位置是黑色节点的孩子,那么无需做出调整;如果插入位置是红色节点的孩子,那么就违反了不能有连续红色节点这个性质,此时我们需要做出调整。

调整

调整分为两种情况

情况一:

假设我们插入的节点就是cur,此时我们需要做出调整,直接旋转(不行),变色?使p和u节点变成黑色怎么样?是的,当前我们看到的确实平衡了,但是如果这只是子树呢?其他路径是不是就不平衡了,所以我们把g节点变成红色,这样就可以了。

但是什么情况下我们才这么做呢?如果u是黑色节点呢?如果u不存在呢?我们还能这样变吗?

所以这样做是因为uncle节点存在且为红色,这就是情况一。

情况二:

那么显然第二种情况就是叔叔不存在或者叔叔为黑色节点,那么这为什么算是一种情况呢?我们画图来看:(叔叔不存在)

这种情况我们如何对节点进行调整?

此时我们可以进行p节点的左单旋,然后进行g节点的右单旋,最后对cur节点和g节点进行变色,cur节点变黑色,g节点变红色:

或者是这样:(叔叔存在且为黑色)

先进行一次情况一的调整:(叔叔存在且为红色) 

剩下这种情况我们就需要进行双旋,首先将p节点进行左旋,然后将g节点进行右旋,之后是这样的:

然后我们进行变色:cur节点变黑,g节点变红,完成双旋:

代码:

我们的逻辑是这样的:

cur是我们插入的新节点,par是cur的父节点,我们要调整的情况是:cur为红色,且父节点为红色,当我们调整后,cur = par;par = par->_parent;如果此时cur已经是根节点,那么par为空,我们结束调整,并将根节点的颜色变成黑色。

进入循环后,首先判断par是grandfather的左节点还是右节点,因为我们需要知道叔叔是g节点的哪个孩子(左还是右),我们主要是看叔叔节点来判断如何进行旋转(左左,左右,右左,右右)。

(当然,也可以先进行判断叔叔是否存在,颜色是什么,然后再判断他是g节点的左还是右,这都是可以的(但是这样做每一个if里都要判断左右,还是比较麻烦的))

        while (par && par->_color == RED)
		{
			Node* grandfather = par->_parent;

			if (grandfather->_left == par)
			{
				Node* uncle = grandfather->_right;

				//情况一:鼠鼠存在且为红色
				if (uncle && uncle->_color == RED)
				{
					par->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					par = cur->_parent;
				}
				//情况二:鼠鼠不存在或者鼠鼠为黑色(可以一同处理)
				else
				{
					//左左,右单旋
					if (cur == par->_left)
					{
						RotateR(grandfather);

						grandfather->_color = RED;
						par->_color = BLACK;
					}
					//左右,左右双旋
					else
					{
						RotateL(par);
						RotateR(grandfather);

						cur->_color = BLACK;
						grandfather->_color = RED;
					}

					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;

				//情况一:鼠鼠存在且为红色
				if (uncle && uncle->_color == RED)
				{
					par->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					par = cur->_parent;
				}
				//情况二:鼠鼠不存在或者鼠鼠为黑色(可以一同处理)
				else
				{
					//右右,左单旋
					if (cur == par->_right)
					{
						RotateL(grandfather);

						grandfather->_color = RED;
						par->_color = BLACK;
					}
					//右左,右左双旋
					else
					{
						RotateR(par);
						RotateL(grandfather);

						cur->_color = BLACK;
						grandfather->_color = RED;
					}

					break;
				}
			}		
		}
		_root->_color = BLACK;

		return true;
	}

    //左单旋
	void RotateL(Node* par)
	{
		Node* subR = par->_right;
		Node* subRL = subR->_left;

		par->_right = subRL;
		if (subRL)
			subRL->_parent = par;

		Node* p_par = par->_parent;
		subR->_left = par;
		par->_parent = subR;

		if (p_par == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (p_par->_left == par)
			{
				p_par->_left = subR;
				subR->_parent = p_par;
			}
			else
			{
				p_par->_right = subR;
				subR->_parent = p_par;
			}
		}
	}

	//右单旋
	void RotateR(Node* par)
	{
		Node* subL = par->_left;
		Node* subLR = subL->_right;

		par->_left = subLR;
		if (subLR)
			subLR->_parent = par;

		Node* p_par = par->_parent;
		subL->_right = par;
		par->_parent = subL;

		if (p_par == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (p_par->_left == par)
			{
				p_par->_left = subL;
				subL->_parent = p_par;
			}
			else
			{
				p_par->_right = subL;
				subL->_parent = p_par;
			}
		}
	}

整体代码

#pragma once
#include <iostream>
using namespace std;

enum Color
{
	RED,
	BLACK
};

template<class K, class T>
struct RBTreeNode
{
	struct RBTreeNode* _left;
	struct RBTreeNode* _right;
	struct RBTreeNode* _parent;
	pair<K, T> _p;
	int _color;

	RBTreeNode(const K& key, const T& val)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _color(RED)
		, _p(key, val)
	{}
};

template<class K, class T>
class RBTree
{
	typedef RBTreeNode<K, T> Node;
public:
	bool Insert(const K& key, const T& val)
	{
		Node* newnode = new Node(key, val);

		if (_root == nullptr)
		{
			_root = newnode;
			_root->_color = BLACK;
			return true;
		}

		Node* cur = _root;
		Node* par = nullptr;
		while (cur)
		{
			par = cur;

			if (cur->_p.first == key) return false;
			else if (cur->_p.first < key) cur = cur->_right;
			else cur = cur->_left;
		}

		if (par->_p.first > key) par->_left = newnode;
		else par->_right = newnode;

		newnode->_parent = par;

		cur = newnode;
		while (par && par->_color == RED)
		{
			Node* grandfather = par->_parent;

			if (grandfather->_left == par)
			{
				Node* uncle = grandfather->_right;

				//情况一:鼠鼠存在且为红色
				if (uncle && uncle->_color == RED)
				{
					par->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					par = cur->_parent;
				}
				//情况二:鼠鼠不存在或者鼠鼠为黑色(可以一同处理)
				else
				{
					//左左,右单旋
					if (cur == par->_left)
					{
						RotateR(grandfather);

						grandfather->_color = RED;
						par->_color = BLACK;
					}
					//左右,左右双旋
					else
					{
						RotateL(par);
						RotateR(grandfather);

						cur->_color = BLACK;
						grandfather->_color = RED;
					}

					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;

				//情况一:鼠鼠存在且为红色
				if (uncle && uncle->_color == RED)
				{
					par->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					par = cur->_parent;
				}
				//情况二:鼠鼠不存在或者鼠鼠为黑色(可以一同处理)
				else
				{
					//右右,左单旋
					if (cur == par->_right)
					{
						RotateL(grandfather);

						grandfather->_color = RED;
						par->_color = BLACK;
					}
					//右左,右左双旋
					else
					{
						RotateR(par);
						RotateL(grandfather);

						cur->_color = BLACK;
						grandfather->_color = RED;
					}

					break;
				}
			}		
		}
		_root->_color = BLACK;

		return true;
	}

	bool IsBlance()
	{
		if (_root && _root->_color == RED)
			return false;

		int standard_black_num = 0;

		Node* cur = _root;
		while (cur)
		{
			if (cur->_color == BLACK)
				standard_black_num++;

			cur = cur->_left;
		}

		return _IsBlance(_root, 0, standard_black_num);
	}

	void Inorder()
	{
		return _Inorder(_root);
	}

private:

	// 检查这棵树是否是红黑树
	// 1、遵守根节点为黑色
	// 2、遵守不能有连续的红色节点
	// 3、每条路径黑色节点数量相同
	// 
	// 遵守最长路径不超过最短路径的两倍,最长路径为一黑一红,最短路径为全黑。
	bool _IsBlance(Node *root, int real_black_num, int standard_black_num)
	{
		if (root == nullptr)
		{
			if (real_black_num != standard_black_num)
				return false;

			return true;
		}

		if (root != _root && root->_color == RED && root->_parent->_color == RED)
			return false;

		if (root->_color == BLACK)
			real_black_num++;

		return _IsBlance(root->_left, real_black_num, standard_black_num)
			&& _IsBlance(root->_right, real_black_num, standard_black_num);
	}

	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;

		_Inorder(root->_left);
		cout << root->_p.first << " : " << root->_p.second << endl;
		_Inorder(root->_right);
	}

	//左单旋
	void RotateL(Node* par)
	{
		Node* subR = par->_right;
		Node* subRL = subR->_left;

		par->_right = subRL;
		if (subRL)
			subRL->_parent = par;

		Node* p_par = par->_parent;
		subR->_left = par;
		par->_parent = subR;

		if (p_par == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (p_par->_left == par)
			{
				p_par->_left = subR;
				subR->_parent = p_par;
			}
			else
			{
				p_par->_right = subR;
				subR->_parent = p_par;
			}
		}
	}

	//右单旋
	void RotateR(Node* par)
	{
		Node* subL = par->_left;
		Node* subLR = subL->_right;

		par->_left = subLR;
		if (subLR)
			subLR->_parent = par;

		Node* p_par = par->_parent;
		subL->_right = par;
		par->_parent = subL;

		if (p_par == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (p_par->_left == par)
			{
				p_par->_left = subL;
				subL->_parent = p_par;
			}
			else
			{
				p_par->_right = subL;
				subL->_parent = p_par;
			}
		}
	}

	Node* _root = nullptr;

};

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-03-13 07:40:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-13 07:40:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-13 07:40:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-13 07:40:03       20 阅读

热门阅读

  1. 股票市场预测模型:未来趋势的智能分析工具

    2024-03-13 07:40:03       21 阅读
  2. Singularity(三)| 将docker转化为singularity容器

    2024-03-13 07:40:03       23 阅读
  3. 软件工程---原型评价

    2024-03-13 07:40:03       19 阅读
  4. iOS 17.4报错: libopencore-amrnb.a[arm64]

    2024-03-13 07:40:03       22 阅读
  5. 人工智能领域从原理详细总结chatgpt的prompt方法

    2024-03-13 07:40:03       22 阅读
  6. html5&css&js代码 013 常见布局

    2024-03-13 07:40:03       21 阅读
  7. 使用docker搭建chromium

    2024-03-13 07:40:03       21 阅读
  8. linux系统docker容器编写dockerfile文件

    2024-03-13 07:40:03       20 阅读