[C++]---红黑树性质及简单模拟实现

本文介绍了红黑树的基本概念、性质、结构和结点定义,重点讲解了红黑树的插入操作及其与AVL树的比较。红黑树在保持平衡的同时,插入效率较高,常用于C++ STL、Java库和Linux内核等。
摘要由CSDN通过智能技术生成

目录

 

红黑树的概念

红黑树的性质

红黑树实现

红黑树结构

红黑树结点定义

红黑树的插入

判断是否是红黑树

红黑树与AVL树的比较,应用

整体代码:


红黑树的概念

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

红黑树的性质

  1. 每个结点不是红色就是黑色,根节点是黑色的。
  2. 如果一个节点是红色的,则它的两个孩子结点是黑色的,也就是 红色节点不能连续,黑色节点可以连续。
  3. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点。
  4. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点),为了让路径划分更加清晰。

一棵红黑树 : 最长路径:红黑相间    最短路径:全黑

红黑树实现

红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 _parent 域指向红黑树的根节点,_left域指向红黑树中最小的节点,_right域指向红黑树中最大的节点,如下:

红黑树结点定义

enum COLOR
{
	BLACK,
	RED
};
//节点存放<k,v>数据
template <class K,class V>
struct RBNode
{
	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)
		, _color(RED)
	{}
};

红黑树的插入

相比于AVL树,插入比较简单,效率比较高,红黑树比AVL树的调整次数要少。

1.按照二叉搜索的树规则插入新节点

2.检测新节点插入后,红黑树的性质是否造到破坏,判断有没有连续的红色结点

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了不能有连续的红色节点的性质,因此就需要进行讨论调整。

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

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

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

情况三: cur为红,p为红,g为黑,u不存在/u为黑

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;
		//按照key值确定位置, key不能重复
		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)
	{
		// cur, parent, gfather, uncle
		parent = cur->_parent;
		pNode gfather = parent->_parent;
		if (gfather->_left == parent)
		{
			pNode uncle = gfather->_right;
			//uncle 存在且为红
			if (uncle && uncle->_color == RED)
			{
				//修改颜色
				parent->_color = uncle->_color = BLACK;
				gfather->_color = RED;
				//继续向上更新
				cur = gfather;
			}
			else{
				//如果存在双旋的场景,可以先进行一次单旋,使它变成单旋的场景
				if (cur == parent->_right)
				{
					RotateL(parent);
					swap(cur, parent);
				}
				//右旋
				RotateR(gfather);
				//修改颜色
				parent->_color = BLACK;
				gfather->_color = RED;
				//停止调整
				break;
			}
		}
		else
		{
			pNode uncle = gfather->_left;
			if (uncle && uncle->_color == RED)
			{
				//修改颜色
				uncle->_color = parent->_color = BLACK;
				gfather->_color = RED;
				cur = gfather;
			}
			else{
				//判断是否有双旋的场景
				if (cur == parent->_left)
				{
					RotateR(parent);
					swap(cur, parent);
				}
				RotateL(gfather);
				parent->_color = BLACK;
				gfather->_color = RED;
				break;
			}
		}

	}
	//根的颜色始终是黑的: _header->_parent
	_header->_parent->_color = BLACK;
	//更新 _header->_left, _header->_right
	_header->_left = leftMost();
	_header->_right = rightMost();
	return true;
}
 

判断是否是红黑树

红黑树的检测分为两步:

  • 1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  • 2. 检测其是否满足红黑树的性质
	bool isRBTree()
	{
		pNode root = _header->_parent;
		if (root == nullptr)
			return true;
		if (root->_color == RED)
		{
			cout << "根节点必须是黑色的!" << endl;
			return false;
		}
		//根节点是黑色
		//需要判断每条路径上黑色个数相同
		//可以先任意遍历一条路径 比如走最右路径。查找black数量
		pNode cur = root;
		int blackCount = 0;
		while (cur)
		{
			if (cur->_color == BLACK)
				++blackCount;
			cur = cur->_right;
		}
		int k = 0;
		return _isRBTree(root, k ,blackCount);
	}

	bool  _isRBTree(pNode root, int curBlackCount, int totalBlackCout)//curBlackCount:走到当前节点黑色个数
	{
		//每条路径上黑色个数相同//没有连续红色结点
		//一条路径走完
		if (root == nullptr)
		{
			if (curBlackCount != totalBlackCout)
			{
				cout << "每条路径黑色结点个数不同" << endl;
				return false;
			}
			return true;

		}

红黑树与AVL树的比较,应用

红黑树与AVL树对比

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

红黑树的应用

  • 1. C++ STL库 -- map/set、mutil_map/mutil_set
  • 2. Java 库
  • 3. linux内核
  • 4. 其他一些库

整体代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<time.h>
#include<utility>
#include<iostream>
using namespace std;
enum COLOR
{
	BLACK,
	RED
};
//节点存放<k,v>数据
template <class K,class V>
struct RBNode
{
	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)
		, _color(RED)
	{}
};
 
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;
	}


	/*
	红黑树插入:
	1.相对于AVL树,插入比较简单,且效率高,红黑树比AVL树调整次数要少
	2.二叉树进行插入
	3.判断有没有连续的红色结点
	如果有:
	a:只需要修改颜色: uncle为红色
	b:修改颜色,旋转:u不存在、存在且为黑
	单旋:cur,parent在gfather的同一边
	双旋:cur,parent不在gfather的同一边,首先经过一次单璇,交换指针,转化为上面单璇场景,
	没有:
	不需要做任何操作,插入结束。
	*/

	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;
			//按照key值确定位置, key不能重复
			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)//当前不是根,并且你的父亲是红色
		{
			//cur:当前节点,parent:父亲节点, gfather:祖父节点,uncle:叔叔节点
			parent = cur->_parent;
			pNode gfather = parent->_parent;
			if (gfather->_left == parent)
			{
				pNode uncle = gfather->_right;
				//uncle 存在且为红
				if (uncle && uncle->_color == RED)
				{
					//修改颜色
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//继续向上更新
					cur = gfather;
				}
				else
				{
					//如果存在双旋的场景,可以先进行一次单旋,使它变成单旋的场景
					if (cur == parent->_right)
					{
						RotateL(parent);
						swap(cur, parent);
					}
					//右旋
					RotateR(gfather);
					//修改颜色
					parent->_color = BLACK;
					gfather->_color = RED;
					//停止调整
					break;
				}

			}
			//gfather->_right == parent
			else
			{
				pNode uncle = gfather->_left;
				if (uncle && uncle->_color == RED)
				{
					//修改颜色
					uncle->_color = parent->_color = BLACK;
					gfather->_color = RED;
					cur = gfather;
				}
				else
				{
					//判断是否有双旋的场景
					if (cur == parent->_left)
					{
						//以parent右旋
						RotateR(parent);
						//交换指针
						swap(cur, parent);
					}
					//以gfather 左旋
					RotateL(gfather);
					//修改颜色
					parent->_color = BLACK;
					gfather->_color = RED;
					//停止调整
					break;
				}
			}
		}

		//根的颜色始终是黑的 根:_header->_parent
		_header->_parent->_color = BLACK;

		//更新 _header->_left, _header->_right
		_header->_left = leftMost();
		_header->_right = rightMost();
		return true;
	}
	pNode leftMost()
	{
		pNode cur = _header->_parent;
		while (cur && cur->_left != nullptr)
		{
			cur = cur->_left;
		}
		return cur;
	}

	pNode rightMost()
	{
		pNode cur = _header->_parent;
		while (cur && cur->_right != nullptr)
		{
			cur = cur->_right;
		}
		return cur;
	}

	void RotateR(pNode parent)
	{
		pNode subL = parent->_left;
		pNode subLR = subL->_right;

		// 1
		subL->_right = parent;
		// 2
		parent->_left = subLR;
		// 3
		if (subLR)
			subLR->_parent = parent;
		// 4,  5
		if (parent != _header->_parent)
		{
			// subL <---> parent->parent
			pNode gParent = parent->_parent;
			if (gParent->_left == parent)
				gParent->_left = subL;
			else
				gParent->_right = subL;
			subL->_parent = gParent;
		}
		else
		{
			//更新根节点
			_header->_parent = subL;
			//subL->_parent = nullptr;
			subL->_parent = _header;
		}
		// 6
		parent->_parent = subL;

	}

	void RotateL(pNode parent)
	{
		pNode subR = parent->_right;
		pNode subRL = subR->_left;

		subR->_left = parent;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		if (parent != _header->_parent){
			pNode gParent = parent->_parent;
			if (gParent->_left == parent)
				gParent->_left = subR;
			else
				gParent->_right = subR;
			subR->_parent = gParent;
		}
		else
		{
			_header->_parent = subR;
			//根的父节点不是nullptr
			//subR->_parent = nullptr;
			subR->_parent = _header;
		}
		parent->_parent = subR;
	}

	void inOrder()
	{
		_inOrder(_header->_parent);
		cout << endl;
	}

	void _inOrder(pNode root)
	{
		if (root){
			_inOrder(root->_left);
			cout << "< " << root->_value.first << "--->" << root->_value.second << "> ";
			_inOrder(root->_right);
		}
	}

	bool isRBTree()
	{
		pNode root = _header->_parent;
		if (root == nullptr)
			return true;
		if (root->_color == RED)
		{
			cout << "根节点必须是黑色的!" << endl;
			return false;
		}
		//根节点是黑色
		//需要判断每条路径上黑色个数相同
		//可以先任意遍历一条路径 比如走最右路径。查找black数量
		pNode cur = root;
		int blackCount = 0;
		while (cur)
		{
			if (cur->_color == BLACK)
				++blackCount;
			cur = cur->_right;
		}
		int k = 0;
		return _isRBTree(root, k ,blackCount);
	}

	bool  _isRBTree(pNode root, int curBlackCount, int totalBlackCout)//curBlackCount:走到当前节点黑色个数
	{
		//每条路径上黑色个数相同//没有连续红色结点
		//一条路径走完
		if (root == nullptr)
		{
			if (curBlackCount != totalBlackCout)
			{
				cout << "每条路径黑色结点个数不同" << endl;
				return false;
			}
			return true;

		}

		if (root->_color == BLACK)
			++curBlackCount;
		
		//没有红色连续
		pNode parent = root->_parent;
		if ( parent->_color == RED && root->_color == RED)
		{
			cout << "有红色连续的结点" << endl;
			return false;
		}
		return  _isRBTree(root->_left, curBlackCount, totalBlackCout) && _isRBTree(root->_right, curBlackCount, totalBlackCout);
	}

private:
	pNode _header;
};

void testRBTree()
{
	RBTree<int, int> rbt;
	rbt.insert(make_pair(1, 1));
	rbt.insert(make_pair(10, 1));
	rbt.insert(make_pair(-1, 1));
	rbt.insert(make_pair(-2, 1));
	rbt.insert(make_pair(100, 1));
	rbt.insert(make_pair(19, 1));
	rbt.insert(make_pair(21, 1));
	rbt.inOrder();
	cout << rbt.isRBTree() << endl;
}

void test()
{
	srand(time(nullptr));
	int n;
	cin >> n;
	RBTree<int, int> rbt;
	for (int i = 0; i < n;++i)
	{
		int key = rand();
		rbt.insert(make_pair(key, key));
		//cout << key << " ";
	}
	cout << endl;
	cout << "isREDTree: " << rbt.isRBTree() << endl;
}

int main()
{
	//testRBTree();
	test();
	system("pause");
	return 0;
}



 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值