STL set 和 map

一、标准库中 set 和 multiset 的使用

在这里插入图片描述

在这里插入图片描述

set 是一颗 K 模型的红黑树,可以存储任意类型,multiset 和 set 的区别是 multiset 允许插入重复值,set 不允许

模板参数 T 表示存储元素的类型,Compare 表示存储元素的比较方法,Alloc 是空间配置器,一般不用传

set 和 multiset 常用接口使用:

#include <iostream>
#include <set>

using namespace std;

void setUsing()
{
	// set 不允许插入重复值
	// insert 返回值为 pair<iterator, bool>
	// 如果插入的值不存在,则返回值 pair 中的 first 指向新插入的元素,second 设置为 true
	// 如果插入的值已存在,则返回值 pair 中的 first 指向已有的元素,second 设置为 false
	set<int> s;
	cout << s.insert(3).second << " ";
	cout << s.insert(1).second << " ";
	cout << s.insert(1).second << " ";
	cout << s.insert(4).second << " ";
	cout << s.insert(2).second << " ";
	cout << s.insert(1).second << " ";
	cout << endl; // 输出 1 1 0 1 1 0

	// set 不允许修改元素内容,因此 iterator 和 const_iterator 是相同的
	set<int>::iterator sit = s.begin();
	while (sit != s.end())
	{
		// *sit *= 10;
		cout << *sit << " ";
		++sit;
	}
	cout << endl; // 输出 1 2 3 4

	// find 成功返回指向该元素的迭代器,失败返回 end()
	sit = s.find(1);
	if (sit != s.end()) cout << *sit << endl; // 输出 1

	// 返回 set 中元素的数目
	cout << s.count(1) << endl; // 输出 1
}

void multisetUsing()
{
	// multiset 允许插入重复值
	std::multiset<int> s;
	s.insert(3);
	s.insert(1);
	s.insert(1);
	s.insert(4);
	s.insert(2);
	s.insert(1);

	for (auto e : s) cout << e << " ";
	cout << endl; // 输出 1 1 1 2 3 4

	// find 成功返回指向该元素的迭代器,失败返回 end()
	std::set<int>::iterator sit = s.find(1);
	if (sit != s.end()) cout << *sit << endl; // 输出 1

	// 返回 set 中元素的数目
	cout << s.count(1) << endl; // 输出 3
}

int main()
{
	setUsing();
	cout << endl;

	multisetUsing();
	cout << endl;

	return 0;
}

二、标准库中 map 和 multimap 的使用

在这里插入图片描述

在这里插入图片描述

map 是一颗 KV 模型的红黑树,可以存储任意类型,multimap 和 map 的区别是 multimap 允许插入 key 值重复的 <key, value> 键值对,map 不允许

模板参数 Key 表示 <key, value> 中 key 的类型,T 表示 <key, value> 中 value 的类型,Compare 表示存储元素的比较方法,Alloc 是空间配置器,一般不用传

map 和 multimap 常用接口使用:

#include <iostream>
#include <map>
#include <string>

using namespace std;

void mapUsing1()
{
	// map 不允许插入 key 值重复的 <key, value> 键值对
	// insert 返回值为 pair<iterator, bool>
	// 如果插入的 key 值不存在,则返回值 pair 中的 first 指向新插入的元素,second 设置为 true
	// 如果插入的 key 值已存在,则返回值 pair 中的 first 指向已有的元素,second 设置为 false
	map<string, string> dict;
	cout << dict.insert(make_pair("sort", "排序")).second << " ";
	cout << dict.insert(make_pair("string", "字符串")).second << " ";
	cout << dict.insert(make_pair("count", "计数")).second << " ";
	cout << dict.insert(make_pair("string", "\"字符串\"")).second << " ";
	cout << endl; // 输出 1 1 1 0

	// map 不允许修改元素内容,因此 iterator 和 const_iterator 是相同的
	map<string, string>::iterator mit = dict.begin();
	while (mit != dict.end())
	{
		cout << mit->first << ":" << mit->second << " ";
		++mit;
	}
	cout << endl; // 输出 count:计数 sort:排序 string:字符串 

	// find 通过 key 查找,成功返回指向该元素的迭代器,失败返回 end() 
	mit = dict.find("string");
	if (mit != dict.end()) cout << mit->second << endl; // 输出 字符串

	// 返回 map 中 key 的数目
	cout << dict.count("string") << endl; // 输出 1

	// operator[key] 等价于 ( *(( this->insert(make_pair(key, mapped_type())) ).first) ).second
	// 如果 key 存在,则返回对应的 <key, value> 中 value 的引用
	// 如果 key 不存在,则先插入 <key, mapped_type()> ,再返回 <key, mapped_type()> 中 value 的引用(这里的 mapped_type 是指 value 的类型)
	dict["string"] = "\"字符串\"";
	dict["right"] = "右边";
	for (auto& e : dict) cout << e.first << ":" << e.second << " ";
	cout << endl; // 输出 count:计数 right:右边 sort:排序 string:"字符串" 
}

// 统计每种水果出现的次数
void mapUsing2()
{
	string fruit[] = {"西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨"};
	map<string, int> fruitCount;

	for (auto &e : fruit) fruitCount[e]++;
	for (auto &e : fruitCount) cout << e.first << ":" << e.second << " ";
	cout << endl; // 输出 梨:1 苹果:5 西瓜:4 香蕉:2
}

void multimapUsing()
{
	// multimap 允许插入 key 值重复的 <key, value> 键值对
	std::multimap<std::string, std::string> m;
	m.insert(std::make_pair("string", "字符串"));
	m.insert(std::make_pair("string", "\"字符串\""));

	for (auto &e : m) cout << e.first << ":" << e.second << " ";
	cout << endl; // 输出 string:字符串 string:"字符串" 

	// 返回 map 中 key 的数目
	cout << m.count("string") << endl; // 输出 2
}

int main()
{
	mapUsing1();
	cout << endl;

	mapUsing2();
	cout << endl;

	multimapUsing();
	cout << endl;

	return 0;
}

三、set 和 map 底层红黑树的模拟实现

#pragma once

#include <utility>

namespace starrycat
{
	// 结点的颜色
	enum Color { RED, BLACK };

	// 红黑树的结点
	template<class VALUE>
	struct RBTreeNode
	{
		VALUE _data;
		RBTreeNode<VALUE>* _left;
		RBTreeNode<VALUE>* _right;
		RBTreeNode<VALUE>* _parent;
		Color _color;	// 结点的颜色

		RBTreeNode<VALUE>(const VALUE& data = VALUE())
			: _data(data)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _color(RED)	// 为了方便树的结构调整,新结点默认为红色
		{}
	};

	template<class T, class Ref, class Ptr>
	struct __rb__iterator
	{
		typedef RBTreeNode<T> Node;
		typedef __rb__iterator<T, Ref, Ptr> Self;
		typedef __rb__iterator<T, T&, T*> iterator;

		__rb__iterator<T, Ref, Ptr>(Node* node) : _node(node) {}
		__rb__iterator<T, Ref, Ptr>(const iterator& it) : _node(it._node) {}

		bool operator==(const Self& s) const { return _node == s._node; }
		bool operator!=(const Self& s) const { return _node != s._node; }
		Ref operator*() const { return _node->_data; }
		Ptr operator->() const { return &_node->_data; }

		void increment()
		{
			if (_node->_right)
			{
				// 根据中序遍历(左根右),如果右子树存在,则迭代器的下一个位置为右子树的最左结点
				Node* cur = _node->_right;
				while (cur->_left) cur = cur->_left;

				_node = cur;
			}
			else
			{
				// 根据中序遍历(左根右),如果右子树不存在,则迭代器的下一个位置为左子树包含当前结点的最近祖先结点
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_right == cur)
				{
					cur = parent;
					parent = parent->_parent;
				}

				_node = parent;
			}
		}

		Self& operator++()
		{
			increment();
			return *this;
		}

		Self operator++(int)
		{
			Self tmp = *this;
			increment();
			return tmp;
		}

		void decrement()
		{
			if (_node->_left)
			{
				// 根据中序遍历(右根左),如果左子树存在,则迭代器的下一个位置为左子树的最右结点
				Node* cur = _node->_left;
				while (cur->_right) cur = cur->_right;

				_node = cur;
			}
			else
			{
				// 根据中序遍历(右根左),如果左子树不存在,则迭代器的下一个位置为右子树包含当前结点的最近祖先结点
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_left == cur)
				{
					cur = parent;
					parent = parent->_parent;
				}

				_node = parent;
			}
		}

		Self& operator--()
		{
			decrement();
			return *this;
		}

		Self operator--(int)
		{
			Self tmp = *this;
			decrement();
			return tmp;
		}

		Node* _node;
	};

	template<class K>
	struct Less
	{
		bool operator()(const K& key1, const K& key2)
		{
			return key1 < key2;
		}
	};

	// 在查找删除数据时,map 需要用 K 作为参数的类型
	// T 是红黑树结点存储的类型,作为 set 的底层时只存储 key,作为 map 的底层时存储 <const key, value>
	// 在插入数据时,set 可以直接比较红黑树结点中的 data,而 map 则需要比较红黑树结点中的 data.first
	// 如果是 set,KEYOFT(data) 返回 data,如果是 map,KEYOFT(data) 返回 data.first
	template<class K, class T, class KEYOFT, class COMPARE = Less<K>>
	class RBTree
	{
		typedef RBTreeNode<T> Node;
	public:
		typedef __rb__iterator<T, T&, T*> iterator;
		typedef __rb__iterator<T, const T&, const T*> const_iterator;
	public:
		RBTree<K, T, KEYOFT, COMPARE>()
			: _root(nullptr)
		{}

		iterator begin()
		{
			// 根据中序遍历(左根右),迭代器的起始位置为树的最左结点 
			Node* cur = _root;
			while (cur && cur->_left) cur = cur->_left;

			return cur;
		}

		iterator end()
		{
			return nullptr;
		}

		const_iterator begin() const
		{
			// 根据中序遍历(左根右),迭代器的起始位置为树的最左结点 
			Node* cur = _root;
			while (cur->_left) cur = cur->_left;

			return cur;
		}

		const_iterator end() const
		{
			return nullptr;
		}

		// 插入
		std::pair<iterator, bool> Insert(const T& data)
		{
			KEYOFT kot;
			COMPARE cmp;

			// 按照二叉搜索树的方式插入结点,保证该树插入结点之后还是二叉搜索树
			if (_root == nullptr)
			{
				_root = new Node(data);
				_root->_color = BLACK;
				return std::make_pair(iterator(_root), true);;
			}

			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cmp(kot(data), kot(cur->_data)))
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cmp(kot(cur->_data), kot(data)))
				{
					parent = cur;
					cur = cur->_right;
				}
				else return std::make_pair(iterator(cur), false);
			}

			Node* newnode = new Node(data);
			cur = newnode;
			if (cmp(kot(data), kot(parent->_data))) parent->_left = cur;
			else parent->_right = cur;

			cur->_parent = parent;

			// 更新颜色
			while (parent && parent->_color == RED)
			{
				Node* grandfather = parent->_parent;
				if (grandfather->_left == parent)
				{
					Node* uncle = grandfather->_right;
					
					// u 存在且为红
					// u 不存在或存在且为黑
					if (uncle && uncle->_color == RED)
					{
						grandfather->_color = RED;
						parent->_color = BLACK;
						uncle->_color = BLACK;

						// 继续判断是否违反了红黑树的性质
						cur = grandfather;
						parent = grandfather->_parent;
					}
					else
					{
						// p 为 g 的左,cur 为 p 的左 右单旋
						// p 为 g 的左,cur 为 p 的右 先左旋再右旋
						if (parent->_left == cur)
						{
							RotateR(grandfather);
							grandfather->_color = RED;
							parent->_color = BLACK;
						}
						else
						{
							RotateL(parent);
							RotateR(grandfather);
							grandfather->_color = RED;
							cur->_color = BLACK;
						}
					}
				}
				else
				{
					Node* uncle = grandfather->_left;

					// u 存在且为红
					// u 不存在或存在且为黑
					if (uncle && uncle->_color == RED)
					{
						grandfather->_color = RED;
						parent->_color = BLACK;
						uncle->_color = BLACK;

						// 继续判断是否违反了红黑树的性质
						cur = grandfather;
						parent = grandfather->_parent;
					}
					else
					{
						// p 为 g 的右,cur 为 p 的右 左单旋
						// p 为 g 的右,cur 为 p 的左 先右旋再左旋
						if (parent->_right == cur)
						{
							RotateL(grandfather);
							grandfather->_color = RED;
							parent->_color = BLACK;
						}
						else
						{
							RotateR(parent);
							RotateL(grandfather);
							grandfather->_color = RED;
							cur->_color = BLACK;
						}
					}
				}
			}

			_root->_color = BLACK;
			return std::make_pair(iterator(newnode), true);
		}

		iterator Find(const K& key)
		{
			KEYOFT kot;
			COMPARE cmp;

			Node* cur = _root;
			while (cur)
			{
				if (cmp(key, kot(cur->_data))) cur = cur->_left;
				else if (cmp(kot(cur->_data), key)) cur = cur->_right;
				else return cur;
			}

			return nullptr;
		}

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

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

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

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

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

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

			subR->_left = parent;
			parent->_parent = subR;

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

	private:
		Node* _root;
	};
}

四、set 类 和 map 类的模拟实现

set 类 和 map 类常用接口模拟实现:

// test.cc
#include "set.hpp"
#include "map.hpp"

int main()
{
	starrycat::setTest1();

	starrycat::mapTest1();
	starrycat::mapTest2();
	
	return 0;
}

// set.hpp
#pragma once

#include "RBTree.h"
#include <iostream>
#include <utility>

namespace starrycat
{
	template <class K>
	struct SET_KEYOFT
	{
		const K &operator()(const K &key)
		{
			return key;
		}
	};

	template <class K>
	class set
	{
	public:
		// set 不允许修改元素内容,因此 iterator 和 const_iterator 是相同的
		typedef typename RBTree<K, K, SET_KEYOFT<K>>::const_iterator iterator;
		typedef typename RBTree<K, K, SET_KEYOFT<K>>::const_iterator const_iterator;

	public:
		// 红黑树底层实现了 iterator 构造 const_iterator
		iterator begin() const { return _rb.begin(); }
		iterator end() const { return _rb.end(); }
		std::pair<iterator, bool> insert(const K &key) { return _rb.Insert(key); }

	private:
		RBTree<K, K, SET_KEYOFT<K>> _rb;
	};

	void setTest1()
	{
		set<int> s;
		int a[] = {16, 3, 7, 11, 9, 26, 18, 14, 15};
		for (auto e : a) s.insert(e);

		set<int>::iterator sit = s.begin();
		while (sit != s.end())
		{
			// *sit *= 10;
			std::cout << *sit << " ";
			++sit;
		}
		std::cout << std::endl; // 输出 3 7 9 11 14 15 16 18 26
	}
}

// map.hpp
#pragma once

#include "RBTree.h"
#include <iostream>
#include <string>

namespace starrycat
{
	template<class K, class V>
	struct MAP_KEYOFT
	{
		const K& operator()(const std::pair<const K, V>& data)
		{
			return data.first;
		}
	};

	template<class K, class V>
	class map
	{
	public:
		typedef typename RBTree<K, std::pair<const K, V>, MAP_KEYOFT<K, V>>::iterator iterator;
		typedef iterator const_iterator;
	public:
		iterator begin() { return _rb.begin(); }
		const_iterator begin() const { return _rb.begin(); }
 		iterator end() { return _rb.end(); }
 		const_iterator end() const { return _rb.end(); }
		std::pair<iterator, bool> insert(const std::pair<K, V>& data) { return _rb.Insert(data); }
		V& operator[](const K& key) { return ( *(( insert(std::make_pair(key, V())) ).first) ).second; }

	private:
		RBTree<K, std::pair<const K, V>, MAP_KEYOFT<K, V>> _rb;
	};

	void mapTest1()
	{
		map<std::string, std::string> dict;
		std::cout << dict.insert(std::make_pair("sort", "排序")).second << " ";
		std::cout << dict.insert(std::make_pair("string", "字符串")).second << " ";
		std::cout << dict.insert(std::make_pair("count", "计数")).second << " ";
		std::cout << dict.insert(std::make_pair("string", "\"字符串\"")).second << " ";
		std::cout << std::endl; // 输出 1 1 1 0

		map<std::string, std::string>::iterator mit = dict.begin();
		while (mit != dict.end())
		{
			std::cout << mit->first << ":" << mit->second << " ";
			++mit;
		}
		std::cout << std::endl; // 输出 count:计数 sort:排序 string:字符串 

		dict["string"] = "\"字符串\"";
		dict["right"] = "右边";
		for (auto& e : dict) std::cout << e.first << ":" << e.second << " ";
		std::cout << std::endl; // 输出 count:计数 right:右边 sort:排序 string:"字符串" 
	}

	// 统计每种水果出现的次数
	void mapTest2()
	{
		std::string fruit[] = {"西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨"};
		map<std::string, int> fruitCount;

		for (auto &e : fruit) fruitCount[e]++;
		for (auto &e : fruitCount) std::cout << e.first << ":" << e.second << " ";
		std::cout << std::endl; // 输出 梨:1 苹果:5 西瓜:4 香蕉:2
	}
}
  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值