【C++深度探索】红黑树实现Set与Map的封装

news2024/9/29 11:33:20
🔥 个人主页:大耳朵土土垚
🔥 所属专栏:C++从入门至进阶

这里将会不定期更新有关C/C++的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉

文章目录

  • 前言
  • 1. 修改红黑树
  • 2. 迭代器
    • ✨const迭代器
  • 3. map的[]访问
  • 4. set和map封装
    • ✨修改后的红黑树
    • ✨map类
    • ✨set类
  • 5. 结语

前言

  前面我们学习过map、set、multimap、multiset的使用,这四种容器都是使用红黑树作为其底层结构。红黑树AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),但是红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对AVL树而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

  今天我们就可以利用之前实现过的红黑树来对C++STL库中的set和map进行模拟实现。


1. 修改红黑树


  我们之前模拟实现过红黑树,插入的节点是键值对pair类型,而如果要使用红黑树来对set和map封装的话,set存储的应该是单个值,而不是键值对,所以我们就需要对红黑树进行修改,使得set和map都能使用:

  1. 首先红黑树存储节点的类需要从只能存储键值对改为能够存储任意数据
template<class T>
struct RBTreeNode
{
	T _data;	//存放数据,不再只能存放键值对:pair<K, V> _kv;	
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;	//保存颜色

	RBTreeNode(const T& data)
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}
};
  1. 相应的,红黑树的模板参数也需要修改:

 修改前:

//红黑树类
template<class K, class V>
class RBTree
{
public:
	typedef RBTreeNode<K, V> Node;
	Node* Find(const K& key);//查找函数
	
private:
	Node* _pHead = nullptr;
};

 因为节点类只有一个模板参数了,所以红黑树两个模板参数有点多余,但是如果将红黑树的模板参数也改为一个,如下面代码所示:

//红黑树类
template<class T>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
	Node* Find(const T& key);//?查找函数
	
private:
	Node* _pHead = nullptr;
};

 那么在实现查找上面代码中的Find函数时,对于map查找时Find函数参数就得传一个完整的键值对,我们不可能把完整的键值对全部传过去查找,这样查找就没有意义,我们只需要获得键值对的键查找到相应的键值对即可,所以我们还应该传一个模板参数:

template<class K, class T>//K存储键,T存储键值对
class RBTree
{
public:
	typedef RBTreeNode<T> Node;//传入键值对
	Node* Find(const K& key);//查找函数,只传键
	
private:
	Node* _pHead = nullptr;
};

这样对于不同函数的需求就可以传入不同的模板参数了🥳🥳

 如果是map存储的是键值对,我们就可以往红黑树的两个模板参数中传入一个键和一个键值对:

//map类
template<class K,class V>
class map {
private:
	RBTree<K, pair<K,V>> _t;
}

这样红黑树的第一个模板参数帮助我们解决查找、删除函数传参的问题,第二个则是必须的,除了存储数据外,插入等函数也需要第二个模板参数

 如果是set存储的不是键值对,我们也可以复用上面的代码,传入两个一样的参数即可:

//set类
template<class K>
class set{
private:
	RBTree<K, K> _t;
}

虽然set看起来传了两个一模一样的参数是无意义的,但是这样就可以实现对红黑树的复用,不用单独为了set再写一个红黑树了,如下图所示:


  1. 插入函数的参数也得从键值对改为任意数据:
bool Insert(const T& data)//之前:bool Insert(const pair<K, V>& data)

 除此以外,我们在寻找插入的位置时不可避免需要比较节点中存储的_data的大小:

while (cur)
	{
		if (cur->_kv.first > data.first)//使用键值对的键比较大小
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_kv.first < data.first)//使用键值对的键比较
		{
			parent = cur;
			cur = cur->_right;
		}
		else
			return false;//没找到返回false
	}
	
	//2.找到,插入节点
	Node* newnode = new Node(data);
	//判断插入父节点左侧还是右侧
	if (parent->_kv.first > data.first)//使用键值对的键比较
		parent->_left = newnode;
	else
		parent->_right = newnode;

 我们发现之前插入键值对都是使用键值对的键来比较大小,当我们将红黑树改成可以存储任意数据后,就不支持上述比较大小的方式了。
 因此为了实现代码的复用,我们需要传入一个新的模板参数,以便在不同情况下都能按照我们需要的方式进行比较,因为如果是map需要通过键来比较,set则直接通过数据进行比较,所以我们可以在set类和map类中各定义一个类来获取要比较的数据,再传给红黑树:

//map类
template<class K,class V>
class map {
//定义一个类获取键值对里面的键
struct MapKeyOfT {
	const K& operator()(const pair<K, V>& data)
	{
		return data.first;
	}
};

private:
		RBTree<K, pair<K,V>, MapKeyOfT> _t;//传给红黑树

}
//set类
template<class K>
class set{
//也定义一个类返回data即可,虽然有点多此一举,但是可以实现代码复用
struct SetKeyOfT {
	const K& operator()(const K& data)
	{
		return data;
	}
};

private:
	RBTree<K, K, SetKeyOfT> _t;//传给红黑树
}

看起来set定义的类似乎做了无用功,但是这一切都是为了能够实现红黑树的复用,map多传了一个参数,set也要多传。


 那么红黑树的模板参数也要修改一下:

template<class K, class T,class KeyOfT>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
	
private:
	Node* _pHead = nullptr;
};

 这样我们在插入函数进行比较时,就可以通过类模板KeyOfT定义一个对象然后使用括号来获取需要进行比较的数了:

bool Insert(const T& data)
{
	KeyOfT kot;//使用类模板,定义一个对象

	//1.先找到插入位置
	//如果是空树
	...
	//如果不是空树
	...
	Node* cur = _pHead;
	Node* parent = nullptr;
	while (cur)
	{
		if (kot(cur->_data) > kot(data))//使用括号获取想要比较的值进行比较
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kot(cur->_data) < kot(data))//使用括号获取想要比较的值进行比较
		{
			parent = cur;
			cur = cur->_right;
		}
		else
			return false;//没找到返回false
	}
	
	//2.找到,插入节点
	Node* newnode = new Node(data);
	//判断插入父节点左侧还是右侧
	if (kot(parent->_data) > kot(data))//使用括号获取想要比较的值进行比较
		parent->_left = newnode;
	else
		parent->_right = newnode;

	//更新newnode父节点和颜色
	...
	}

这样就可以使用类模板定义的对象通过重载括号获取你想要进行比较的值,如果插入类型是键值对,那么就获得键值对中的键进行比较;如果不是键值对,括号返回的也是数据本身直接进行比较即可;这样不同的数据都可以进行比较🥳🥳


2. 迭代器


  红黑树的迭代器与链表的迭代器类似,都是使用指针来构造:

//迭代器
template<class T>
struct RBTreeIterator {
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T> self;
	
	T& operator*()
	{
		return _node->_data;
	}
	
	T* operator->()
	{
		return &(_node->_data);
	}
	
	bool operator!=(const self& t)
	{
		return _node != t._node;
	}
	
	bool operator==(const self& t)
	{
		return _node == t._node;
	}
	
	self& operator++()
	{//...}
	
	
	//构造
	RBTreeIterator(Node* node)
		:_node(node)
	{}
	Node* _node;
};

✨对于operator++

 因为红黑树中序遍历是有序的,所以如果使用迭代器遍历,我们也希望它是有序的,例如下面的红黑树:



 如果当前节点是13,它的右子树不为空,那么下一个节点就应该是它右子树的最左节点15;如果当前节点是15,它的右子树为空,那么下一个节点就应该是它的父亲17,但是这是因为15是它父亲的左孩子;如果是右孩子,那么说明这棵子树已经遍历完了,需要继续向上找父亲,代码如下:

self& operator++()
{
	if (_node->_right)
	{
		// 右不为空,右子树最左节点就是中序第一个
		Node* leftMost = _node->_right;
		while (leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		_node = leftMost;
	}
	else
	{
		// 孩子是父亲左的那个祖先
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_right)
		{
			cur = parent;
			parent = cur->_parent;
		}

		_node = parent;
	}
	return *this;
}

✨const迭代器


  相较于普通迭代器,const迭代器指向的内容不可以被修改,也就是说operator*operator->返回值不可以修改,所以只要在其返回值前加const修饰即可,为了与普通迭代器复用同一个迭代器类,我们需要在迭代器类的模板参数中多穿两个:

//迭代器
template<class T,class Ref,class Ptr>
struct RBTreeIterator {
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> self;
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &(_node->_data);
	}
};

  这样在红黑树中如果是普通迭代器就传T,T&,T*这三个模板参数,如果是const迭代器就传T,const T&,const T*这三个模板参数,这样就很好限制了const迭代器修改指向的内容:

template<class K, class T,class KeyOfT>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T,T&,T*> Iterator;
	typedef RBTreeIterator<T, const T&, const T*> ConstIterator;

	//普通迭代器
	Iterator Begin()
	{
		Node* leftMost = _pHead;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		return Iterator(leftMost);
	}

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

	//const迭代器
	ConstIterator Begin() const 
	{
		Node* leftMost = _pHead;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		return ConstIterator(leftMost);
	}

	ConstIterator End()	const
	{
		return ConstIterator(nullptr);
	}
	
private:
		Node* _pHead = nullptr;
};


  有了迭代器之后,Find查找函数的返回值就可以使用迭代器了🥳🥳:

// 检测红黑树中是否存在值为key的节点,存在返回该节点的迭代器,否则返回End()
Iterator Find(const K& key)
{
	KeyOfT kot;//使用类模板,定义一个对象

	//1.先找到插入位置
	//如果是空树
	if (_pHead == nullptr)
		return End();

	
	//如果不是空树
	Node* cur = _pHead;
	while (cur)
	{
		if (kot(cur->_data) > key)
		{
			cur = cur->_left;
		}
		else if (kot(cur->_data) < key)
		{
			cur = cur->_right;
		}
		else
			return Iterator(cur);//找到返回
	}

	return End();//没找到返回End()
}
	

这里同样要注意,查找函数需要通过比较特定的值来实现,所以我们可以利用之前在插入函数中使用的类模板继续创建一个对象来获取需要比较的值。


3. map的[]访问


  在map的使用介绍中,我们知道可以用[]来访问修改键值对以及插入数据:

//迭代器构造
std::vector<pair<string, string>> v = { {"上", "up"}, { "下", "down"}, { "左", "left"}, { "右", "right"} };
std::map<string, string> m(v.begin(), v.end());

//[]访问
cout << m["上"] << endl;
cout << m["下"] << endl;
cout << m["左"] << endl;
cout << m["右"] << endl;

//[]修改
m["右"] += "tutu";

//[]插入
m["中"] = "center";

cout << endl;
cout << "修改插入后:" << endl;
std::map<string, string>::iterator it = m.begin();
while (it != m.end())
{
	cout << it->first << ":" << it->second << endl;

	++it;
}
cout << endl;

结果如下:


  map的[]能够插入数据是因为其复用了插入函数,如果[]里面引用的值不存在map中就会插入并返回键值对的值,存在就直接返回键值对的值,而插入函数中恰好会先寻找合适的插入位置,并返回bool值,所以我们只需对插入函数返回的值进行修改:
我们将插入函数的返回值设为pair类型,如果插入成功就返回新节点的迭代器和true;如果插入失败,那么map中肯定以及有相同的值,那么返回该位置的迭代器和false

这样在[]中就可以复用插入函数完成插入和修改操作了:

	V& operator[](const K& key)
	{
		pair<iterator, bool> ret = insert(make_pair(key, V()));
		return ret.first->second;
	}

插入函数只需要修改返回值即可:

pair<Iterator,bool> Insert(const T& data)
{
	//...简写
	//1.插入成功
		return make_pair(Iterator(newnode),true);
	//2.插入失败
		return make_pair(Iterator(node), false);//node:已有位置的指针
}


4. set和map封装


  我们对于map和set封装需要各种新开一个头文件map.hset.h来进行,并且都需要包含RBTree.h头文件,放在自己的命名空间内,避免与STL标准库中的map和set弄混。

✨修改后的红黑树

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

//枚举颜色
enum Colour
{
	RED,
	BLACK
};

//节点类
template<class T>
struct RBTreeNode
{
	T _data;	//存放数据
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;	//保存颜色

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

//迭代器
template<class T,class Ref,class Ptr>
struct RBTreeIterator {
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> self;
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &(_node->_data);
	}
	self& operator++()
	{
		if (_node->_right)
		{
			// 右不为空,右子树最左节点就是中序第一个
			Node* leftMost = _node->_right;
			while (leftMost->_left)
			{
				leftMost = leftMost->_left;
			}

			_node = leftMost;
		}
		else
		{
			// 孩子是父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}
		return *this;
	}
	bool operator!=(const self& t)
	{
		return _node != t._node;
	}
	bool operator==(const self& t)
	{
		return _node == t._node;
	}
	//构造
	RBTreeIterator(Node* node)
		:_node(node)
	{}
	Node* _node;
};


//红黑树类
template<class K, class T,class KeyOfT>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T,T&,T*> Iterator;
	typedef RBTreeIterator<T,const T&,const T*> ConstIterator;


	Iterator Begin()
	{
		Node* leftMost = _pHead;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		return Iterator(leftMost);
	}
	
	//直接使用空指针构造
	Iterator End()
	{
		return Iterator(nullptr);
	}

	//const迭代器
	ConstIterator Begin() const 
	{
		Node* leftMost = _pHead;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}

		return ConstIterator(leftMost);
	}

	ConstIterator End()	const
	{
		return ConstIterator(nullptr);
	}
	
	RBTree() = default;
	
	//拷贝构造
	RBTree(const RBTree<K, T,KeyOfT>& t)
	{
		_pHead = Copy(t._pHead);
	}
	//赋值运算符重载
	RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
	{
		swap(_pHead, t._pHead);
		return *this;
	}
	
	//析构函数
	~RBTree()
	{
		Destroy(_pHead);
		_pHead = nullptr;
	}
	
	//求树的高度
	int Height()
	{
		return _Height(_pHead);
	}
	
	//树中节点个数
	int Size()
	{
		return _Size(_pHead);
	}


	
	// 注意:为了简单起见,本次实现红黑树不存储重复性元素
	pair<Iterator,bool> Insert(const T& data)
	{
		KeyOfT kot;//使用类模板,定义一个对象

		//1.先找到插入位置
		//如果是空树
		
		if (_pHead == nullptr)
		{
			Node* newnode = new Node(data);
			newnode->_col = BLACK;
			_pHead = newnode;
			return make_pair(Iterator(newnode),true);
		}
		//如果不是空树
		Node* cur = _pHead;
		Node* parent = nullptr;
		while (cur)
		{
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return make_pair(Iterator(nullptr), false);//没找到返回false
		}
		
		//2.找到,插入节点
		Node* newnode = new Node(data);
		//判断插入父节点左侧还是右侧
		if (kot(parent->_data) > kot(data))
			parent->_left = newnode;
		else
			parent->_right = newnode;

		//更新newnode父节点和颜色
		newnode->_parent = parent;
		if (parent->_col == BLACK)
		{
			//父节点是黑色,插入成功
			return make_pair(Iterator(newnode),true);
		}
		if (parent->_col == RED)
		{
			//父节点是红色

			cur = newnode;
			while (parent && parent->_col == RED)
			{
				Node* grandparent = parent->_parent;//parent是红色,肯定不是根节点,所以grandparent不是空节点,而且是黑色
				
				//找叔叔节点
				Node* uncle = grandparent->_left;
				if (parent == grandparent->_left)
					uncle = grandparent->_right;
				
				if (uncle&&uncle->_col == RED)
				{
					//如果uncle是红色
					//将unlcle和parent节点都变为黑色,grandparent节点变为红色
					parent->_col = uncle->_col = BLACK;//即可保证所有路径上黑色一样多
					grandparent->_col = RED;
				
					//继续往上更新
					cur = grandparent;
					parent = cur->_parent;
				}
				else if (uncle==nullptr||uncle->_col == BLACK)
				{
					//如果uncle不存在或者存在且为黑色
					if (grandparent->_left == parent && parent->_left == cur)
					{
						//右单旋,再将grandparent改为红色,parent改为黑色
						RotateR(grandparent);
						grandparent->_col = RED;
						parent->_col = BLACK;
					}
					else if (grandparent->_right == parent && parent->_right == cur)
					{
						//左单旋,再将grandparent改为红色,parent改为黑色
						RotateL(grandparent);
						grandparent->_col = RED;
						parent->_col = BLACK;
					}
					else if (grandparent->_right == parent && parent->_left == cur)
					{
						RotateR(parent);//先右单旋
						RotateL(grandparent);//再左单旋
						//再将grandparent的颜色改为红色,cur改为黑色
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
					else if (grandparent->_left == parent && parent->_right == cur)
					{
						RotateL(parent);//先左单旋
						RotateR(grandparent);//后右单旋
						//再将grandparent的颜色改为红色,parent改为黑色
						grandparent->_col = RED;
						cur->_col = BLACK;
					}
					else
						assert(false);
					
					//插入成功,跳出循环
					break;
				}
			}

		}
		_pHead->_col = BLACK;
		return make_pair(Iterator(newnode), true);
	}

	// 检测红黑树是否为有效的红黑树
	bool IsValidRBTRee()
	{
		if (_pHead == nullptr)
			return true;

		if (_pHead->_col == RED)
		{
			return false;
		}

		// 先求一条路径上黑色节点数量作为参考值
		int refNum = 0;
		Node* cur = _pHead;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refNum;
			}

			cur = cur->_left;
		}

		return Check(_pHead, 0, refNum);
	}

	// 检测红黑树中是否存在值为key的节点,存在返回该节点的迭代器,否则返回End()
	Iterator Find(const K& key)
	{
		KeyOfT kot;//使用类模板,定义一个对象

		//1.先找到插入位置
		//如果是空树
		if (_pHead == nullptr)
			return End();

		
		//如果不是空树
		Node* cur = _pHead;
		while (cur)
		{
			if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else
				return Iterator(cur);//找到返回
		}

		return End();//没找到返回End()
	}

	


	

private:
	
	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 RotateL(Node* parent)
	{

		Node* cur = parent->_right;

		//将cur的左边给parent的右边,cur的左边再指向parent
		parent->_right = cur->_left;
		cur->_left = parent;

		//链接cur与parent的父节点
		if (parent->_parent == nullptr)
		{
			//如果parent是根节点
			cur->_parent = nullptr;
			_pHead = cur;
		}
		else if (parent->_parent->_left == parent)
			parent->_parent->_left = cur;
		else
			parent->_parent->_right = cur;


		//更新父节点
		cur->_parent = parent->_parent;
		parent->_parent = cur;
		if (parent->_right)//判断parent的右边是否存在
			parent->_right->_parent = parent;

		
	}
	// 右单旋
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;

		//将cur的右边给parent的左边,cur的右边再指向parent
		parent->_left = cur->_right;
		cur->_right = parent;

		//链接cur与parent的父节点
		if (parent->_parent == nullptr)
		{
			//如果parent是根节点
			cur->_parent = nullptr;
			_pHead = cur;
		}
		else if (parent->_parent->_left == parent)
			parent->_parent->_left = cur;
		else
			parent->_parent->_right = cur;


		//更新父节点
		cur->_parent = parent->_parent;
		parent->_parent = cur;
		if (parent->_left)
			parent->_left->_parent = parent;
	}

	int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

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

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

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

		Node* newRoot = new Node(root->_data);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);

		return newRoot;
	}


	Node* _pHead = nullptr;
};


✨map类

#include"RBTree.h"
namespace tutu {
	template<class K,class V>
	class map {
	public:
		//定义类获取data.first进行比较
		struct MapKeyOfT {
			const K& operator()(const pair<K, V>& data)
			{
				return data.first;
			}
		};

		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::ConstIterator const_iterator;

		iterator begin()
		{
			return _t.Begin();
		}
		iterator end()
		{
			return _t.End();
		}
		const_iterator begin() const
		{
			return _t.Begin();
		}
		const_iterator end()	const
		{
			return _t.End();
		}
		pair<iterator,bool> insert(const pair<K,V>& data)
		{
			return _t.Insert(data);
		}
		
		iterator find(const K& key)
		{
			return _t.Find(key);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		RBTree<K, pair<K,V>, MapKeyOfT> _t;
	};


//测试函数
	void Print(const map<string, int>& m)
	{
		map<string, int>::const_iterator it = m.begin();
		while (it != m.end())
		{
			cout << it->first << ":" << it->second << endl;

			++it;
		}
		cout << endl;
	}
	void TestMap()
	{
		map<string,int> m;
		m.insert({ "111",1 });
		m.insert({ "444",4 });
		m.insert({ "222",2 });
		m.insert({ "666",6 });
		m.insert({ "333",3 });
		m.insert({ "777",7 });
		
		map<string, int>::iterator it = m.begin();
		while (it != m.end())
		{
			cout << it->first << ":" << it->second << endl;
			
			++it;
		}
		cout << endl;
		/*cout << "“777”:" << endl;
		cout << m.find("777")->second << endl;*/
		cout << "const迭代器遍历:" << endl;
		Print(m);
	}
}

结果如下:


✨set类

#include"RBTree.h"

namespace tutu {

	template<class K>
	class set{
	public:
		struct SetKeyOfT {
			const K& operator()(const K& data)
			{
				return data;
			}
		};
	
		typedef typename RBTree<K, K, SetKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::ConstIterator const_iterator;

		iterator begin()
		{
			return _t.Begin();
		}
		iterator end()
		{
			return _t.End();
		}
		const_iterator begin() const
		{
			return _t.Begin();
		}
		const_iterator end()	const
		{
			return _t.End();
		}
		pair<iterator, bool> insert(const K& data)
		{
			return _t.Insert(data);
		}

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


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

结果如下:


5. 结语

  mapset的底层都是使用一颗红黑树来实现的,然后在外面套了一层壳,为了能够更好的实现代码复用,我们对红黑树进行了很多修改还使用了仿函数,最终用一颗红黑树模拟实现除了mapset。以上就是今天所有的内容啦~ 完结撒花 ~🥳🎉🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1991888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Comfyui线稿转绘工作流 一键掉进彩色世界

前言 在数字艺术的世界里&#xff0c;线稿转绘无疑是将创意从简单的线条变成生动图画的关键一步。而在众多工具中&#xff0c;ComfyUI的线稿转绘工作流以其便捷和高效&#xff0c;赢得了众多艺术家的青睐。 本文将详细介绍ComfyUI线稿转绘工作流的应用场景和优势&#xff0c;…

C语言典型例题29

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 习题2.6 设圆半径r1.5&#xff0c;圆柱高h3&#xff0c;求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积。 scanf输入结果&#xff0c;输出计算结果&#xff0c;输出时要有文字说明&#xff0c;取小数点后两位数…

优质电器/机械岗位推荐:经验不限大厂直招,薪资最高30K!

本周优质电器/机械岗位推荐&#xff0c;涵盖C、自动化、开发、安卓开发、项目管理等岗位&#xff0c;经验不限&#xff0c;更有大厂直招岗位&#xff0c;薪资最高30K&#xff01;&#xff01; 抓紧投递&#xff0c;早投早入职&#xff01; &#x1f447;点击职位名称查看详情…

数据结构(java实现)——优先级队列,堆

文章目录 优先级队列堆堆的概念堆的模拟实现创建堆入堆判满删除判空获取栈顶元素 创建堆两种方式的时间复杂度堆排序java提供的PriorityQueue类基本的属性关于PriorityQueue类的三个构造方法关于PriorityQueue类中&#xff0c;入堆方法是怎样实现的&#xff1f;PriorityQueue注…

基于GA-PSO-SVM算法的混沌背景下微弱信号检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 支持向量机&#xff08;SVM&#xff09; 4.2 GA-PSO-SVM算法 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) svm参数取值对检测性能的影响&#xff1a; SVM,PSO,…

华为Mate 60系列推送最新132版本有惊喜!你收到了吗?

华为Mate 60系列又悄悄搞事情&#xff0c;带了个大惊喜——手机获推HarmonyOS 4.2.0.132&#xff0c;一起来看看优化了什么&#xff1f; 首先更新的就是相机效果&#xff0c;从色彩精准度与拍照预览一致性上更加精益求精&#xff0c;上大分&#xff01; 色彩呈现更准确&#x…

基于Java中的SSM框架实现校园图书在线阅读系统项目【项目源码+论文说明】

基于Java中的SSM框架实现校园图书在线阅读系统演示 摘要 随着计算机的广泛应用和推广&#xff0c;越来越多的传统人工管理模式逐渐被线上化平台系统所替代&#xff0c;高校作为知识的学府在计算机的应用上非常广泛&#xff0c;同时随着近年来高校信息化建设的不断提升&#xf…

PDF编辑不求人!这三款免费版编辑器助你轻松搞定!

作为一名办公室文员&#xff0c;每天和PDF文件打交道那是家常便饭。打印合同、整理报告、编辑资料&#xff0c;PDF文件简直就是我的工作小伙伴。不过&#xff0c;说起编辑PDF&#xff0c;那可真是个技术活。以前&#xff0c;我总是为这事儿头疼&#xff0c;直到遇见了几款pdf编…

JVM知识总结(内存结构)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 内存模型 内存结构 堆&#xff1a; 存放对象实例, 几乎所有的对象…

Candance Allegro 入门教程笔记:如何绘制原理图和原理图库?

文章目录 一、用 Capture CIS 17.4 绘制原理图库 一、用 Capture CIS 17.4 绘制原理图库 1、安装完Candance Allegro 17.4后&#xff0c;我们所需的原理图工具以及PCB工具如下图所示&#xff1a;【Capture CIS 17.4】和【PCB Editor 17.4】 2、打开原理图工具&#xff1a;Captu…

React性能之--如何避免组件重复渲染?

在react中&#xff0c;我们会发现存在组件会重复渲染&#xff0c;虽然说如果项目不大的话&#xff0c;这点影响不大&#xff0c;但是我们还是尽量避免组件渲染比较好&#xff0c;养成好习惯&#xff0c;尽可能让不管是大小项目&#xff0c;都让性能尽可能优化 。那我们如何避免…

Spring-创建bean

preInstantiateSingletons v1.0 preInstantiateSingletons tips&#xff1a;最终调用getBean使用的是RootBeanDefinition进行构建的 public void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating sin…

圣诞节星星飘落的效果(微信小程序版)

圣诞节快到啦&#xff5e;&#x1f384;&#x1f384;&#x1f384;&#x1f384;咱们也试着做做小程序版本的星星✨飘落效果吧 在微信小程序内如何实现雪花飘落&#xff0c;星星飘落的效果呢&#xff5e; 先来个效果图&#xff1a; 1576672109245.gif 一步一步来&#xff1a;…

【稳定ACM出版、EI检索|武汉场线上线下结合】2024年第五届医学人工智能国际学术会议(ISAIMS 2024,8月13-17)

第五届医学人工智能国际学术会议&#xff08;ISAIMS2024&#xff09;将于2024年8月13-17日于荷兰阿姆斯特丹自由大学召开&#xff0c;国内分会场将于2024年10月25-27日于中国武汉召开。 本届会议将继续围绕人工智能在医学领域的最新研究成果&#xff0c;为来自国内外高等院校、…

智能化的Facebook未来:AI如何重塑社交网络的面貌?

随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;社交网络的面貌正在经历深刻的变革。Facebook&#xff08;现Meta Platforms&#xff09;作为全球最大的社交媒体平台之一&#xff0c;正积极探索如何利用AI技术来提升用户体验、优化内容管理并推动平台创新。…

全红婵夺冠后答记者问,太风趣了

8月6日晚&#xff0c;在巴黎结束的跳水女子10米台比赛中&#xff0c;17岁的中国选手全红婵以425.60分的高分成功卫冕&#xff0c;帮助中国代表团获得第22枚金牌。赛后采访时有记者想煽情&#xff0c;故意问到全红婵的哥哥来现场为她加油&#xff0c;结果小女子竟然毫无表情地回…

【python】Pandas处理Excel表格用法分析与最佳实践

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

AI大模型03:Function Calling

接口Interface &#xff08;1&#xff09;人际交互接口 UI &#xff08;User Interface&#xff09; &#xff08;2&#xff09;应用程序编程接口 API &#xff08;Application Programming Interface&#xff09; 接口能通的关键&#xff1a;是两方都要遵守约定。 &#xff0…

EI检索,2天录用,3天见刊!截稿在即,这本水刊你还不投吗?

点击关注&#xff1a;关注GZH【欧亚科睿学术】&#xff0c;GET完整版2023JCR分区列表&#xff01; &#x1f389; &#x1f389; &#x1f389; &#x1f389; 恭喜&#xff01;这本毕业水刊仅2天录用&#xff01;3天见刊&#xff01; 重要时间节点如下 2024-08-03 Sub…

360安全大模型为什么是“非卖品”?

大模型虽然不是万能的&#xff0c;但是没有大模型又是万万不能的。以AI大模型为动力引擎&#xff0c;AI正在重塑各行各业&#xff0c;并快速“飞入寻常百姓家”。 AI安全 以“模”制“模” 2024年全国两会&#xff0c;“人工智能”首次被写入政府工作报告。报告中提出&#xff…