【C++】map和set的封装

个人主页zxctscl
如有转载请先通知

文章目录

  • [1. map和set不同的模板参数](#1. map和set不同的模板参数)
  • [2. 红黑树的迭代器](#2. 红黑树的迭代器)
    • [2.1 Begin和End](#2.1 Begin和End)
    • [2.2 operator++](#2.2 operator++)
    • [2.3 operator--](#2.3 operator--)
    • [2.4 迭代器封装](#2.4 迭代器封装)
  • 3.改造红黑树
    • [3.1 代码](#3.1 代码)
  • [4. map的模拟实现](#4. map的模拟实现)
  • [5. set的模拟实现](#5. set的模拟实现)

在前面【C++】map和set中谈到map和set的一些基本用法。

1. map和set不同的模板参数

在源码中看看map和set:

当单看,红色部分时候发现两个都一样:

但是这两个是不一样的:

map的key_type和value_type不一样,value_type是一个<key, value>的pair,不同就是在第二个模版参数:

set的key_type和value_type都是key:相当于传了两个key给红黑树

在源码stl_tree.h中:value_field就是第二个模版参数

这里就是泛型编程,这里并没有写死到时是pair还是key。这里value就是一个模板,实例化传的是pair,那么这个红黑树节点存的就是pair,就是map的模型。

2. 红黑树的迭代器

2.1 Begin和End

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,

看一下在stl_set.h里面迭代器是怎么实现的迭代器的:

转到stl_tree.h里面:

里面包含节点的指针,解引用还有加加这些就行:

cpp 复制代码
template<class T, class Ref, class Ptr>

struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

	__RBTreeIterator(Node* node)
		:_node(node)
	{}
};

解引用取里面的数据:就是取当前节点里面的数据返回。

cpp 复制代码
	Ref operator*()
	{
		return _node->_data;
	}

常用的operator->和!=

cpp 复制代码
	Ptr operator->()
	{
		return &_node->_data;
	}

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

需要考虑以前问题:

begin()与end()STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置end()放在最大节点(最右侧节点)的下一个位置 ,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

end是最右节点的下一个,这里没有用头结点,而是用空代表,是有一定的缺陷的。

找最左节点,也就是Begin,如果这棵树是空,那么leftMin就是空,就不会进循环,正好去构造Iterator:

cpp 复制代码
	Iterator Begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return Iterator(leftMin);
	}

end是最右节点的下一个:

cpp 复制代码
	Iterator End()
	{
		return Iterator(nullptr);
	}

2.2 operator++

如果当前节点的右子树不为空,中序下一个访问的节点,右子树的最左节点:

但当右为空,下一个访问,倒着在祖先里找,找到孩子实在父亲左的祖先:

这里it是6,此时1已经访问过了,下一个要访问的就是8:

这里是中序遍历:it是父亲的右,it访问完了,it的父亲也访问完了,此时下一个要访问it的祖先。

当访问到最右边那个节点时候,要把迭代器置空。

cpp 复制代码
	Self& operator++()
	{
		if (_node->_right)
		{
			// 下一个,右树最左节点
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}

			_node = leftMin;
		}
		else
		{
			// 下一个,孩子等于父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

2.3 operator--

左不为空,左子树的中序的最后一个节点(最右节点)

中序遍历:右根左,左边节点访问完了,那么子树就访问完了

11访问完了,减减就访问8:

左为空,找到孩子是父亲右的那个祖先节点:

cpp 复制代码
	Self& operator--()
	{
		if (_node->_left)
		{
			Node* righttMin = _node->_left;
			while (righttMin->_right)
			{
				rightMin = rightMin->_right;
			}

			_node = righttMin;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}
		return *this;

	}
};

2.4 迭代器封装

cpp 复制代码
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

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

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

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

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

	Self& operator++()
	{
		if (_node->_right)
		{
			// 下一个,右树最左节点
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}

			_node = leftMin;
		}
		else
		{
			// 下一个,孩子等于父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			Node* righttMin = _node->_left;
			while (righttMin->_right)
			{
				rightMin = rightMin->_right;
			}

			_node = righttMin;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}
		return *this;

	}
};

3.改造红黑树

因为关联式容器中存储的是<key, value>的键值对,因此k为key的类型,ValueType: 如果是map则为pair<K, V>; 如果是set,则为k,KeyOfValue: 通过value来获取key的一个仿函数类。

在插入中首先要比较data,但是并不知道比较的是不是key,所以就得先找到key,就用一个内部类来解决,如果是map就比较pair的first,如果是set就比较key。

Map的:

cpp 复制代码
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

Set的:

cpp 复制代码
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

3.1 代码

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

enum Colour
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _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 __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

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

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

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

	Self& operator++()
	{
		if (_node->_right)
		{
			// 下一个,右树最左节点
			Node* leftMin = _node->_right;
			while (leftMin->_left)
			{
				leftMin = leftMin->_left;
			}

			_node = leftMin;
		}
		else
		{
			// 下一个,孩子等于父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}
};

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

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

	RBTree() = default;

	RBTree(const RBTree<K, T, KeyOfT>& t)
	{
		_root = Copy(t._root);
	}

	// t2 = t1
	RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~RBTree()
	{
		Destroy(_root);

		_root = nullptr;
	}

	Iterator Begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return Iterator(leftMin);
	}

	Iterator End()
	{
		return Iterator(nullptr);
	}

	ConstIterator End() const
	{
		return ConstIterator(nullptr);
	}

	ConstIterator Begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return ConstIterator(leftMin);
	}

	Iterator Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return Iterator(cur);
			}
		}

		return End();
	}
	
	pair<Iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(Iterator(_root), true);
		}

		KeyOfT kot;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			// K
			// pair<K, V>
			// kot对象,是用来取T类型的data对象中的key
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(Iterator(cur), false);
			}
		}

		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED; // 新增节点给红色
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		// parent的颜色是黑色也结束
		while (parent && parent->_col == RED)
		{
			// 关键看叔叔
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// 叔叔存在且为红,-》变色即可
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 叔叔不存在,或者存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g  
						//   p   u
						// c 
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//      g  
						//   p     u
						//      c 
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					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 // 叔叔不存在,或者存在且为黑
				{
					// 情况二:叔叔不存在或者存在且为黑
					// 旋转+变色
					//      g
					//   u     p
					//            c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//		g
						//   u     p
						//      c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(Iterator(newnode), true);
	}

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

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

		subL->_right = parent;

		Node* ppNode = parent->_parent;
		parent->_parent = subL;

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

			subL->_parent = ppNode;
		}
	}

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

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

		subR->_left = parent;
		Node* ppNode = parent->_parent;

		parent->_parent = subR;

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

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool IsBalance()
	{
		if (_root->_col == RED)
		{
			return false;
		}

		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refNum;
			}

			cur = cur->_left;
		}

		return Check(_root, 0, refNum);
	}

private:
	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* newroot = new Node(root->_data);
		newroot->_col = root->_col;

		newroot->_left = Copy(root->_left);
		if (newroot->_left)
			newroot->_left->_parent = newroot;

		newroot->_right = Copy(root->_right);
		if (newroot->_right)
			newroot->_right->_parent = newroot;

		return newroot;
	}

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

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
		root = nullptr;
	}

	bool Check(Node* root, int blackNum, const int refNum)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			if (refNum != blackNum)
			{
				cout << "存在黑色节点的数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			//cout << root->_kv.first << "存在连续的红色节点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, blackNum, refNum)
			&& Check(root->_right, blackNum, refNum);
	}

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

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

private:
	Node* _root = nullptr;
	//size_t _size = 0;
};

4. map的模拟实现

在插入中首先要比较data,与set不同的是,map中有pair,不知道比较的是不是key,为了找到key,同样用一个内部类来解决

cpp 复制代码
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

在插入时候这里数据是data,并不知道是key还是pair,那么怎么比较大小?

对于map而言,怎么的T传的是pair,这里就要取pair里面的first,就是pair里面的key,就用到MapKeyOfT。

任何在调用仿函数operator(),就调用到了map的operator(),就知道了data是一个pair,就返回firs。

cpp 复制代码
namespace bit
{
	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<const K, V>, MapKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, const K, MapKeyOfT>::ConstIterator const_iterator;

		const_iterator begin() const
		{
			return _t.Begin();
		}

		const_iterator end() const
		{
			return _t.End();
		}

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

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

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

		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<const K, V>, MapKeyOfT> _t;
	};

5. set的模拟实现

在插入中首先要比较data,但是并不知道比较的是不是key,所以就得先找到key,就用一个内部类来解决:

cpp 复制代码
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

在插入时候这里数据是data,并不知道是key还是pair,那么怎么比较大小?

set传了一个SetKeyOfT下来,此时就知道这个T就是一个key,下层的RBTree不知道,但是上层的set知道。所以set的K和第二个模板参数传给下面的T的key

而下面的KeyOfT是一个仿函数,这里的kot就可以调用operate()。他是set的KeyOfT对象,此时调用operate(),就调用到set的operator():

为什么不直接比较?

是因为和map用到的是同一套模板。map是这样,那么set也得这样。

cpp 复制代码
namespace bit
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;

		const_iterator begin() const
		{
			return _t.Begin();
		}

		const_iterator end() const
		{
			return _t.End();
		}

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

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

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}

	private:
		RBTree<K, const K, SetKeyOfT> _t;
	};

有问题请指出,大家一起进步!!!

相关推荐
雾月558 分钟前
LeetCode 3146 两个字符串的排列差
java·数据结构·算法·leetcode
Bro_cat17 分钟前
JavaEE 前后端交互与数据库连接练习
java·服务器·数据库·java-ee·tomcat·交互
ybq1951334543121 分钟前
javaEE-文件操作和IO-文件
java·java-ee
ybq1951334543124 分钟前
javaEE-多线程进阶-JUC的常见类
java·开发语言
blammmp25 分钟前
JavaEE 初阶:线程(2)
java·开发语言
正在绘制中39 分钟前
Java重要面试名词整理(二十):Gateway&SkyWalking
java·面试·gateway·skywalking
qq_4585638141 分钟前
通过excel导入数据
java·excel
意如流水任东西1 小时前
[C++]vector(超详细)
开发语言·c++
南─1 小时前
深入解析 Redisson 分布式限流器 RRateLimiter 的原理与实现
java·分布式·redisson
Harrytsz1 小时前
Visual Studio 2022 C++ gRPC 环境搭建
c++·grpc·visual studio·vcpkg