红黑树的实现mapset的封装

Posted ych9527

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了红黑树的实现mapset的封装相关的知识,希望对你有一定的参考价值。

1.红黑树概念、性质

1.1什么是红黑树

红黑树,是一种二叉搜索树,但是在每个节点上增加了一个存储位,标识节点的颜色,可以是RED或者BLACK。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其它的路径长出两倍(最长路径<=2倍最短路径),因此是接近平衡的

1.2红黑树的性质

1.每个节点不是红色就是黑色的

2.根节点是黑色的

3.没有连续的两个红色节点

4.对于每个节点,从该节点到其后代叶节点的路径上,均包含相同的黑色节点(每条路径上的黑色节点的总数是相同的)

5.每个叶子节点都是黑色的(这里的叶子节点指的是空节点)

1.3性质分析

1.3.1为什么满足1.2的性质,就能保证最长路径不会超过最短路径的两倍

image-20210602161017006

1.3.2与AVL树进行效率比较

设红黑树的黑色节点数为x个,任意一颗红黑树中,总结点的个数N为[x,2x],即查找的效率为 logN~log2N -> logN~log2N 即O(logN)

但是在底层实现之中,AVL是严格的平衡二叉树,红黑树只是近似平衡二叉树,即AVL树要进行更多的旋转操作,因此红黑树的性能更优于AVL树。我们常用的set和map在底层用的就是红黑树

2.红黑树底层构建情况分析

2.1父亲节点为红色,叔叔节点存在,并且为红色

image-20210601164539907

2.2父亲节点为红色,叔叔节点不存在或者存在且为黑色

2.2.1叔叔节点不存在

image-20210601164739373

2.2.2叔叔节点存在,且为黑色

image-20210601164842834

2.3总结

由上述分析可知,红黑树的构建主要分为如下情况

1.父亲节点为黑色,插入的节点为红色,不需要进行操作

2.父亲节点和叔叔节点为红色,插入一个红色的节点后,父亲节点和叔叔节点变为黑色,祖父节点变为红色

3.父亲节点为红色、叔叔节点不存在或者为黑色:

当parent在grandfather左侧时:
cur在parent左侧 -> 以g为中心,进行右旋,并且g变为红色,p变为黑色(右旋)
cur在parent右侧 -> 先以p为中心进行左旋,再以g为中心进行右旋(左右双旋)

当parent在grandfather右侧时:
cur在parent右侧 -> 以g为中心,进行左旋,并且g变为红色,p变为黑色(左旋)
cur在parent左侧 -> 先以p为中心进行右旋,再以g为中心进行左旋(右左双旋)

3.实现代码和验证

3.1实现代码

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

enum Color
{
	RED,
	BLACK
};


template<class K,class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>&kv)
	:_left(nullptr)
	, _right(nullptr)
	, _parent(nullptr)
	, _kv(kv)
	, _col(RED)
	{}

	RBTreeNode<K, V> *_left;
	RBTreeNode<K, V> *_right;
	RBTreeNode<K, V> *_parent;
	pair<K, V> _kv;
	enum  Color _col;
};

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

public:

	void RotateL(Node *parent)//左旋
	{
		Node *subR = parent->_right;//左旋,p的右边一定不为空
		Node *subL = subR->_left;//可能为空
		Node *pparent = parent->_parent;

		//将subR左侧节点链接到p的右侧
		parent->_right = subL;
		if (subL != nullptr)
			subL->_parent = parent;

		//将p链接到subR的左侧
		subR->_left = parent;
		parent->_parent = subR;
		
		//subR与pp的链接
		subR->_parent = pparent;
		if (pparent == nullptr)
			_root = subR;
		else
		{
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
		}

		//颜色更改
		parent->_col = RED;
		subR->_col = BLACK;

	}

	void RotateR(Node *parent)//右旋
	{
		Node *subL = parent->_left;
		Node *subR = subL->_right;
		Node *pparent = parent->_parent;

		parent->_left = subR;
		if (subR != nullptr)
			subR->_parent = parent;

		subL->_right = parent;
		parent->_parent = subL;

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

		//颜色更改
		parent->_col = RED;
		subL->_col = BLACK;

	}

	pair<Node*, bool> Insert(pair<K, V>&kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return make_pair(_root, true);
		}

		//有根节点了
			Node *parent = nullptr;
			Node *cur = _root;

			while (cur)
			{
				if (kv.first > cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kv.first < cur->_kv.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else//相等,,去重
				{
					return make_pair(cur, false);
				}

			}

			//此时进行节点的插入
			cur = new Node(kv);
			Node* newnode = cur;//保存一份,返回用

			if (parent->_kv.first > kv.first)//在左边
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else//在右边
			{
				parent->_right = cur;
				cur->_parent = parent;
			}

			//插入红色的节点,维护规则-》相邻节点不能同样是红色

			while (parent&&parent->_col == RED)//父亲节点不为空,并且为红色,说明此时需要进行调整
			{
				Node *grandfather = parent->_parent;//父亲节点存在且为红色,那么一定不是根节点

					Node *uncle = grandfather->_right;
					if (grandfather->_right == parent)//父亲节点为右,则叔叔节点为左
						uncle = grandfather->_left;

					if (uncle != nullptr&&uncle->_col == RED)//情况1:叔叔节点存在且为红色
					{
						//颜色更改
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;

						//继续向上判断
						cur = grandfather;//新的红节点
						parent = cur->_parent;//
					}
					else//叔叔节点不存在,或者存在为黑色
					{
						if (grandfather->_left == parent)//p在g的左侧
						{
							if (cur == parent->_left)//c在p的左侧,只需要进行右旋
							{
								RotateR(grandfather);
							}
							else//c在p的右侧,先左再右
							{
								RotateL(parent);
								RotateR(grandfather);
							}
						}
						else//p在g的右侧
						{
							if (cur == parent->_right)//c在p的右侧,左旋
							{
								RotateL(grandfather);
							}
							else//先右再左旋
							{
								RotateR(parent);
								RotateL(grandfather);
							}
						}
						break;//旋转之后即完成了要求,不需要再进行判断
					}
			}

		_root->_col = BLACK;//防止根节点被改成红色,根节点赋予黑色,每条路径上都是适用的
		return make_pair(newnode, true);
	}

	//[]重载

	V &operator [](const K &k)//string、int、vector等等都可以是V,是由默认的构造函数的 int()=0
	{
		//insert返回的是pair<node*,bool>

		return  ((Insert(make_pair(k, V())).first)->_kv).second;
	}


	//检测相关

	//遍历
	void _Inorder(Node *root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);

		cout << root->_kv.first << " " << root->_kv.second <<" "<<root->_col<< endl;
		_Inorder(root->_right);

	}

	void Inorder()
	{
		_Inorder(_root);
	}



	//判断是否是红黑树 ->判断一个红色节点的左右子树是否是红色的  -> 判断一个红色节点的父亲是否是红色的

	bool CheckCol(Node* root)
	{
		if (root == nullptr)
			return true;

		if (root->_col == RED)//当前节点为红色
		{
			if (root->_parent != nullptr)
			{
				if (root->_parent->_col == RED)//红色节点的父亲也为红色,则不是红黑树
				{
					cout << root->_kv.first << " " << root->_kv.second <<"颜色判断失败 ->不是红黑树"<< endl;
					return false;
				}
			}
		}

		return CheckCol(root->_left) && CheckCol(root->_right);
	}

	bool CheckBlack(Node *root,int TrueNum,int BlackNum)//统计路径上面的黑色节点
	{
		if (root == nullptr)//表示一条路径走到底了
		{
			return TrueNum == BlackNum;//判断每条路径的黑色节点是否相等
		}
		
		
		if (root->_col == BLACK)
			BlackNum++;

		return
			CheckBlack(root->_left, TrueNum, BlackNum) &&
			CheckBlack(root->_right, TrueNum, BlackNum);
	}


	bool Check()
	{
		Node *cur = _root;
		int TrueNum = 0;
		
		if (_root&&_root->_col == RED)//根不能为红色
		{
			return false;
		}

		while (cur)//统计一条路径上面的黑色节点的个数
		{
			if (cur->_col == BLACK)
				TrueNum++;
			cur = cur->_left;
		}


		return CheckCol(_root) && CheckBlack(_root, TrueNum, 0);

	}
	
private:
	Node *_root = nullptr;

};
#include "rbt.hpp"
#include <vector>


void test1(vector<int>&arr)
{
	RBTree<int, int> t;

	for (auto&e : arr)
	{
		t[e]++;
	}


	t.Inorder();
	cout << "_____校验________" << endl;

	if (t.Check())
		cout << "是红黑树" << endl;
	else
		cout << "不是红黑树" << endl;
}

int main()
{

	cout << "测试1" << endl;
	vector<int>arr1 = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };

	test1(arr1);


	cout << endl << "测试2" << endl;
	vector<int>arr2 = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	test1(arr2);




	system("pause");

	return 0;
}

3.2验证

image-20210602101239773

4.map、set的封装

4.1原码查看分析

image-20210602171714614

4.2构造思路

image-20210602214401784

4.3迭代器++、–的构造

image-20210602214429045

4.4代码实现

#pragma once
#include <iostream>
#include <assert.h>
#include <windows.h>
using namespace std;

enum Color
{
	RED,
	BLACK
};


template<class T>//节点只提供一个T(V)值,构成set和map共用一颗红黑树
struct RBTreeNode
{
	RBTreeNode(const T &t)
	:_left(nullptr)
	, _right(nullptr)
	, _parent(nullptr)
	, _t(t)
	, _col(RED)
	{}

	RBTreeNode<T> *_left;
	RBTreeNode<T> *_right;
	RBTreeNode<T> *_parent;
	T _t;
	enum  Color _col;
};

//迭代器的封装
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
	typedef RBTreeIterator<T, Ref, Ptr> Self;//迭代器++  还是迭代器
	typedef RBTreeNode<T> Node;
	Node *_node;

	RBTreeIterator(Node *node)
		:_node(node)
	{}

	Ref operator *()
	{
		assert(_node != nullptr);
		return _node->_t;
	}

	Ptr operator ->()
	{
		assert(_node != nullptr);
		return &(_node->_t);//返回的是节点的指针的引用,编译器优化了一次箭头
	}

	Self operator ++()//找中序的下一个
	{
		assert(_node != nullptr);

		// 中序的下一个
		if (_node->_right)//右节点不为空,下一个位置就为右树的最左节点
		{
			// 右树的最左节点
			Node* cur = _node->_right;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}

			_node = cur;
		}
		else
		{
			//右边为空,表示以cur->parent为根节点的树,已经访问完毕了
			//此时再向上搜寻,cur不是parent的右孩子的父亲节点
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self operator ++(int)//后置
	{
		assert(_node != nullptr);

		Self rnode= _node;//隐式类型转换

		// 中序的下一个
		if (_node->_right)//右节点不为空,下一个位置就为右树的最左节点
		{
			// 右树的最左节点
			Node* cur = _node->_right;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}

			_node = cur;
		}
		else
		{
			//右边为空,表示以cur->parent为根节点的树,已经访问完毕了
			//此时再向上搜寻,cur不是parent的右孩子的父亲节点
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return rnode;
	}

	Self operator--()
	{
		if (_node == nullptr)//为最后空节点
		{

		}

		else if (_node->_left != nullptr)
		{
			_node = _node->_left;
		}
		else//寻找孩子不是父亲左节点的父亲
		{
			Node *cur = _node;
			Node *parent = cur->_parent;
			while (parent&&parent->_left == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self operator--(int)//后置减减
	{
		Self rnode = _node;

		if (_node->_left != nullptr)
		{
			_node = _node->_left;
		}
		else//寻找孩子不是父亲左节点的父亲
		{
			Node *cur = _node;
			Node *parent = cur->_parent;
			while (parent&&parent->_left == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}

		return rnode;
	}

	bool operator!=(const Self &s)const 
	{
		return _node != s._node;
	}

	bool operator==(const Self &s)const 
	{
		return _node == s._node;
	}
};



//set<K> -> RVTree<K,K>
//map<K,V> -> RBTree<K,pair<const K,V>>

template<class K,class T,class KeyofT>  
//当T是K的时候,传给节点的就是K-> set,当K是pair的时候,传给节点的时候就是pair ->map
//find需要用K值去查找,如果不写K,set知道k是什么,map并不知道
class RBTree
{
	typedef RBTreeNode<T> Node;

public:
	typedef RBTreeIterator<T, T&, T*> Iterator;
	typedef RBTreeIterator<T, const T&, const T*> ConstIterator;

	Iterator begin()//最左边的节点
	{
		Node *cur = _root;
		while (cur&&cur->_left)
		{
			cur = cur->_left;
		}
		return  cur;//隐式类型转换
	}

	Iterator end()//前闭后开区间
	{
		return nullptr;
	}

	ConstIterator begin() const //最左边的节点
	{
		Node *cur = _root;
		while (cur&&cur->_left)
		{
			cur = cur->_left;
		}
		return  Iterator(cur);
	}

	ConstIterator end() const //前闭后开区间
	{
		return Iterator(nullptr);
	}



	void RotateL(Node *parent)//左旋
	{
		Node *subR = parent->_right;//左旋,p的右边一定不为空
		Node *subL = subR->_left;//可能为空
		Node *pparent = parent->_parent;

		//将subR左侧节点链接到p的右侧
		parent->_right = subL;
		if (subL != nullptr)
			subL->_parent = parent;

		//将p链接到subR的左侧
		subR->_left = parent;
		parent->_parent = subR;
		
		//subR与pp的链接
		subR->_parent = pparent;
		if (pparent == nullptr)
			_root = subR;
		else
		{
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
		}

		//颜色更改
		parent->_col = RED;
		subR->_col = BLACK;

	}

	void RotateR(Node *parent)//右旋
	{
		Node *subL = parent->_left;
		Node *subR = subL->_right;
		Node *pparent = parent->_parent;

		parent->_left = subR;
		if (subR != nullptr)
			subR->_parent = parent;

		subL->_right = parent;
		parent->_parent = subL;

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

		//颜色更改
		parent->_col = RED;
		subL->_col = BLACK;

	}

	pair<Node*, bool> Insert(const T &t)
	{
		KeyofT kot;

		if (_root == nullptr)
		{
			_root = new Node(t);
			_root->_col = BLACK;
			return make_pair(_root, true);
		}

		//有根节点了
			Node *parent = nullptr;
			Node *cur = _root;

			while (cur)
			{
				if (kot(cur->_t) < kot(t))//往右边走
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kot(cur->_t) > kot(t))//往左边走
				{
					parent = cur;
					cur = cur->_left;
				}
				else//相等,,去重
				{
					return make_pair(cur, false);
				}

			}

			//此时进行节点的插入
			cur = new Node(t);
			Node* newnode = cur;//保存一份,返回用

			if (kot(parent->_t) > kot(t))//在左边
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else//在右边
			{
				parent->_right = cur;
				cur->_parent = parent;
			}

			//插入红色的节点,维护规则-》相邻节点不能同样是红色

			while (parent&&parent->_col == RED)//父亲节点不为空,并且为红色,说明此时需要进行调整
			{
				Node *grandfather = parent->_parent;//父亲节点存在且为红色,那么一定不是根节点

					Node *uncle = grandfather->_right;
					if (grandfather->_right == parent)//父亲节点为右,则叔叔节点为左
						uncle = grandfather->_left;

					if (uncle != nullptr&&uncle->_col == RED)//情况1:叔叔节点存在且为红色
					{
						//颜色更改
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;

						//继续向上判断
						cur = grandfather;//新的红节点
						parent = cur->_parent;//
					}
					else//叔叔节点不存在,或者存在为黑色
					{
						if (grandfather->_left == parent)//p在g的左侧
						{
							if (cur == parent->_left)//c在p的左侧,只需要进行右旋
							{
								RotateR(grandfather);
							}
							else//c在p的右侧,先左再右
							{
								RotateL(parent);
								RotateR(grandfather);
							}
						}
						else//p在g的右侧
						{
							if (cur == parent->_right)//c在p的右侧,左旋
							{
								RotateL(grandfather);
							}
							else//先右再左旋
							{
								RotateR(parent);
								RotateL(grandfather);
							}
						}
						break;//旋转之后即完成了要求,不需要再进行判断
					}
			}

		_root->_col = BLACK;//防止根节点被改成红色,根节点赋予黑色,每条路径上都是适用的
		return make_pair(newnode, true);
	}

	//[]重载

	Iterator operator[] (const T &t)//string、int、vector等等都可以是V,是由默认的构造函数的 int()=0
	{
		//insert返回的是pair<node*,bool>
		return Insert(t).first;//返回node*的迭代器
	}


private:
	Node *_root = nullptr;

};
#pragma once
#include "rbt.hpp"

namespace my
{
	
	template<class K,class V>
	class map
	{
		struct mapKofT//map作为上层,知道V是什么类型
		{
			const K& operator ()(const pair<const K, V> &kv)
			{
				return kv.first;
			}
		};


	public:
		typedef typename RBTree<K, pair<const K, V>, mapKofT>::Iterator iterator;

		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}

		pair<RBTreeNode<pair<const K, V>>*, bool> insert(const pair<const K, V> &kv)
		{
			return _t.Insert(kv);
		}

		V& operator[](const K &k)
		{
			//方法1:
			//inser 返回pair<RBTreeNode<pair<const K, V>>*, bool>
			//return ((insert(make_pair(k, V())).first)->_t).second;


			//方法2:
			//_t[]返回一个迭代器
			return _t[make_pair(k,V())]->second;
		}

	

	private:
		RBTree<K, pair<const K, V>, mapKofT> _t;

	};

};
#pragma once
#include "rbt.hpp"

namespace my
{
	template<class K>
	class set
	{
		struct setKofT//仿函数
		{
			const K& operator ()(const K &k)
			{
				return k;
			}
		};

	public:

		//typename告诉编译器实例化之后再去找
		//如果不加,这里对Iterator进行类型重定义,但是RBTree没有实例化,因此是找不到的
		typedef typename RBTree<K, K, setKofT>::Iterator iterator;

		iterator begin()//实际上调用的是红黑树的begin
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}


		pair<RBTreeNode<K>*, bool> insert(const K &k )
		{
			return _t.Insert(k);
		}


	private:
		RBTree<K, K, setKofT> _t;//红黑树对象

	};

};
#include "rbt.hpp"
#include "map.h"
#include "set.h"
#include <string>

void test_map()//map测试
{
	my::map<int, int> mp;

	mp.insert(make_pair(1, 1));
	mp.insert(make_pair(2, 2));
	mp.insert(make_pair(3, 2));

	mp[2]++;
	mp[3]++;
	mp[4]++;
	mp[5]++;
	mp[1]++;
	mp[8]++;
	mp[9]++;

	my::map<int, int>::iterator it = mp.begin();

	while (it != mp.end())
	{
		cout << it->first << " " << it->second << endl;
		it++;
	}
}


void test_set()
{
	my::set<string> st;

	st.insert("苹果");
	st.insert("香蕉");
	st.insert("菠萝");
	st.insert("樱桃");
	st.insert("哈密瓜");
	st.insert("西瓜");

	my::set<string>::iterator it = st.begin();

	while (it !=st.end())
	{
		cout << *it << endl;
		it++;
	}
}
int main()
{
	cout << "__________map 测试____________" << endl;
	test_map();
	cout << "__________set 测试____________" << endl;
	test_set();
	return 0;
}

4.5测试结果

image-20210602214749515

以上是关于红黑树的实现mapset的封装的主要内容,如果未能解决你的问题,请参考以下文章

C++红黑树详解并封装实现map和set

红黑树底层迭代器的实现

红黑树底层迭代器的实现

STL详解—— 用一棵红黑树同时封装出map和set

教你轻松理解红黑树的实现及原理

Java实现红黑树