【C++】红黑树模拟实现map和set

本篇基于上篇红黑树的代码来实现:

【C++】红黑树-CSDN博客

关于map和set可以看:​​​​

【C++】map和set的介绍和使用-CSDN博客


改造红黑树

map底层是红黑树的KV模型,set是红黑树的K模型,按理来说,应该设计两种红黑树来模拟实现map和set。但是STL库中并没有这样做,而是复用了同一颗红黑树

它是如何通过一颗红黑树来模拟实现map和set?

通过第二个模板参数Value来控制节点是pair类型,还是Key类型。

map中传的是pair,Value中节点就是pair类型,set中传的是Key类型,Value中节点就是Key类型。

下面我们来改造自己的红黑树:

节点不再是之前确定的pari类型,而是T类型,具体是什么类型,传给T的是pair就是

pair,传给T的是Key就是Key类型。

但是随之而来就会引发一些问题,在插入的时候就会出问题。

之前是比key小往左边走,比key大往右边走。但是现在节点data的类型并不知道。

如果此时,传给节点data的类型为Key,那么可以直接比较不会有问题,如果传给节点data的类型为pari,可以直接比较吗?

STL库中对于pair的比较是这样实现的:

解释:

first小或者first不小second小,它就小。也就是first和second有一个小就小。

这样的比较方式并不是我们想要的比较。我们想要比较只是比较first,跟second并没有关系。

所以针对map和set要设计两种不同的比较方式。

通过仿函数来设计。

如果KOfT是MapKeyOfT,就去调用map的operator()取出pair中的key,如果KOfT是SetKeyOfT,就去调用set的operator()取出key。

红黑树的迭代器

cpp 复制代码
template<class T>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T> Self;
	Node* _node;
	__TreeIterator(Node* node)
		:_node(node)
	{	}
	T& operator*()
	{
		return _node->_data;
	}
	T* operator->()
	{
		return &_node->_data;
	}
	Self& operator++()
	{
		//
	}
	Self& operator--()
	{
		//
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}
    bool operator==(const Self& s)
    {
  	    return _node == s._node;
    }
};
cpp 复制代码
template<class K, class T, class KOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T> iterator;
	iterator begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
    //...
    //...
cpp 复制代码
namespace zxa
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		bool Insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}
	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
	void test_map();
}
cpp 复制代码
namespace zxa
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
		
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

		bool Insert(const K& k)
		{
			return _t.Insert(k);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
	void test_set();
}

map和set只是对红黑树进行了一层封装 。

const迭代器

通过模板参数简单控制一下即可。

cpp 复制代码
template<class K, class T, class KOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T,T&,T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;
cpp 复制代码
template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T,Ref,Ptr> Self;
	Node* _node;
	__TreeIterator(Node* node)
		:_node(node)
	{	}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
    

operator++

节点5++为节点6,节点6++为节点7,节点7++为节点8,节点8++为节点10。

  1. 如果_node的右子树不为空,中序遍历的下一个节点就是右子树的最左节点。
  2. 如果_node的右子树为空,说明_node的子树已经访问完成,下一个节点到它的祖先中找。沿着路径往上,找孩子是它的左的那个祖先。

代码实现:

cpp 复制代码
Self& operator++()
{
	//1.如果右子树不为空,中序的下一个节点就是右子树的最左节点
	if (_node->_right)
	{
		Node* cur = _node;
		Node* subLeft = cur->_right;
		while (subLeft->_left)
		{
			subLeft = subLeft->_left;
		}
		_node = subLeft;
	}
	//2.如果右子树为空,说明_node所在子树已经访问完成,下一个节点到它的祖先中找。
	//沿着路径往上找 孩子是它的左的那个祖先
	else
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && parent->_right == cur)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

operator[ ]

map的operator是调用Insert来实现的。

cpp 复制代码
pair<iterator,bool> Insert(const pair<K, V>& kv)
{
	return _t.Insert(kv);
}

operator[]是去找key对应的value。

cpp 复制代码
V& operator[](const K& key)
{
	pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
	return ret.first->second;
}

关于operator[]的详细分析可以看【C++】map和set的介绍和使用-CSDN博客

map和set的模拟及测试代码

Myset.hpp

cpp 复制代码
#pragma once 

namespace zxa
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
		
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

		pair<iterator,bool> Insert(const K& k)
		{
			return _t.Insert(k);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
	void test_set();
}

Mymap.hpp

cpp 复制代码
#pragma once

namespace zxa
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		pair<iterator,bool> Insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
	void test_map();
}

RBTree.hpp

cpp 复制代码
#pragma once
#include<iostream>

using namespace std;

enum Colour
{
	BLACK,
	RED,
};
template<class T>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	T _data;

	Colour _col;
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{	}
};

template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T,Ref,Ptr> Self;
	Node* _node;
	__TreeIterator(Node* node)
		:_node(node)
	{	}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	Self& operator++()
	{
		//1.如果右子树不为空,中序的下一个节点就是右子树的最左节点
		if (_node->_right)
		{
			Node* cur = _node;
			Node* subLeft = cur->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		}
		//2.如果右子树为空,说明_node所在子树已经访问完成,下一个节点到它的祖先中找。
		//沿着路径往上找 孩子是它的左的那个祖先
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	Self& operator--()
	{
		//...
		return *this;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}
	bool operator==(const Self& s)
	{
		return _node == s._node;
	}
};

template<class K, class T, class KOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T,T&,T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;
	iterator begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
	pair<iterator,bool> Insert(const T& data)
	{
		//按搜索树的方式进行插入
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			//return pair<iterator(_root), true>;
			return make_pair(iterator(_root), true);
		}
		Node* parent = nullptr;
		Node* cur = _root;
		KOfT koft;
		while (cur)
		{
			if (koft(cur->_data) < koft(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (koft(cur->_data) > koft(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}
		cur = new Node(data);
		Node* newnode = cur;
		if (koft(parent->_data) > koft(data))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		//新增节点为红色
		cur->_col = RED;
		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 = cur->_parent;
				}
				else
				{
					//情况三:单旋变为情况二
					if (cur == parent->_right)
					{
						RotateL(parent);
						swap(parent, cur);
					}
					//情况二:有可能为情况三变化而来
					RotateR(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						RotateR(parent);
						swap(parent, cur);
					}
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
					break;
				}
			}
		}
		//永远把根置为黑
		_root->_col = BLACK;
		return make_pair(iterator(cur), true);
	}
	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}
		subR->_left = parent;
		Node* pparent = parent->_parent;
		parent->_parent = subR;
		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
			}
			else
			{
				pparent->_right = subR;
			}
			subR->_parent = pparent;
		}
	}
	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		subL->_right = parent;
		Node* pparent = parent->_parent;
		parent->_parent = subL;
		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
			subL->_parent = pparent;
		}
	}
	iterator Find(const K& key)
	{
		KOfT koft;
		Node* cur = _root;
		while (cur)
		{
			if (koft(cur->_data) < data)
			{
				cur = cur->_right;
			}
			else if (koft(cur->_data) > data)
			{
				cur = cur->_left;
			}
			else
			{
				return iterator(cur);
			}
		}
		return iterator(nullptr);
	}
private:
	Node* _root = nullptr;
};

Test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 

#include"RBTree.hpp"
#include"Mymap.hpp"
#include"Myset.hpp"

//查找水果出现的次数
void zxa::test_map()
{
	string strs[] = { "西瓜","苹果","西瓜","西瓜","西瓜","西瓜","苹果","樱桃" };
	map<string, int> countMap;
	//1、如果水果在map中,则operator[]会插入pair<str,0>,返回映射对象(次数)的引用,对它进行++。
	//2、如果水果不在map中,则operator[]会返回映射对象(次数)的引用,对它进行++。
	for (auto& str : strs)
	{
		countMap[str]++;
	}
	for (auto& e : countMap)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
}

void zxa::test_set()
{
	set<int> s;
	s.Insert(1);
	s.Insert(3);
	s.Insert(4);
	s.Insert(5);
	s.Insert(2);

	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " " << endl;
		++it;
	}
}
int main()
{	
	zxa::test_map();
	zxa::test_set();
	return 0;
}

运行结果

相关推荐
煤泥做不到的!1 小时前
挑战一个月基本掌握C++(第十一天)进阶文件,异常处理,动态内存
开发语言·c++
F-2H1 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
axxy20001 小时前
leetcode之hot100---24两两交换链表中的节点(C++)
c++·leetcode·链表
chenziang11 小时前
leetcode hot100 环形链表2
算法·leetcode·链表
bryant_meng2 小时前
【python】OpenCV—Image Moments
开发语言·python·opencv·moments·图片矩
若亦_Royi2 小时前
C++ 的大括号的用法合集
开发语言·c++
Captain823Jack3 小时前
nlp新词发现——浅析 TF·IDF
人工智能·python·深度学习·神经网络·算法·自然语言处理
资源补给站3 小时前
大恒相机开发(2)—Python软触发调用采集图像
开发语言·python·数码相机
Captain823Jack3 小时前
w04_nlp大模型训练·中文分词
人工智能·python·深度学习·神经网络·算法·自然语言处理·中文分词
m0_748247554 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php