C++STL详解(五)map&set的使用及其模拟实现

前言

之前介绍了红黑树,现在就利用红黑树来模拟实现简化版本的set和map。

0.关联式容器

概念

我们之前接触过的部分容器,比如:vector、list、deque、forward_list(C++11)等,
这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。

关联式容器也是用来存储数据的,但与序列式容器不同的是,
其里面存储的是<key, value>结构的键值对,
数据检索时比序列式容器效率更高

pair

键值对:
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。

比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() 
	: first(T1())
	, second(T2())
	{}
	pair(const T1& a, const T2& b) 
	: first(a)
	, second(b)
	{}
};

树形结构关联式容器

根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。
树型结构的关联式容器主要有四种:map、set、multimap、multiset。
这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

1.set

介绍

set文档介绍
在这里插入图片描述

注意:

  1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
  2. set中插入元素时,只需要插入value即可,不需要构造键值对。
  3. set中的元素不可以重复(因此可以使用set进行去重)。
  4. 使用set的迭代器遍历set中的元素,可以得到有序序列
  5. set中的元素默认按照小于来比较
  6. set中查找某个元素,时间复杂度为: l o g 2 n log_2 n log2n
  7. set中的元素不允许修改
  8. set中的底层使用二叉搜索树(红黑树)来实现

迭代器

void test_set1()
{
	set<int> s;
	s.insert(5);
	s.insert(2);
	s.insert(3);
	s.insert(3);
	s.insert(1);
	s.insert(1);
	s.insert(1);
	s.insert(4);
	s.insert(4);

	// 排序+去重
	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		//*it += 1; // 普通迭代器和const迭代器都是不允许修改的
		cout << *it << " ";
		++it;
	}
	cout << endl; // 1 2 3 4 5

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl; // 1 2 3 4 5
}

注意:普通迭代器和const迭代器都是不允许修改的。这一点我们从底层源代码也能发现端倪。
在这里插入图片描述

find

const_iterator find (const value_type& val) const;
iterator       find (const value_type& val);

在这里插入图片描述

注意使用find的时候,需要结合条件判断

void test_set2()
{
	set<int> s;
	s.insert(4);
	s.insert(5);
	s.insert(2);
	s.insert(1);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(1);

	set<int>::iterator pos = s.find(2);  // O(logN)
	if (pos != s.end())
	{
		cout << "set.find找到了" << endl;
	}
	else
	{
		cout << "set.find找不到" << endl;
	}
	// set.find找到了

	pos = find(s.begin(), s.end(), 20); // O(N) 算法里的查找是暴力查找
	if (pos != s.end())
	{
		cout << "全局find找到了" << endl;
	}
	else
	{
		cout << "全局find找不到" << endl;
	}
	// 全局find找不到
}

set 里面的 find 效率比较高,是 l o g n log n logn 级别的
而 algorithm 里面的 find 效率较低,属于暴力查找, O ( N ) O(N) O(N) 级别

erase

(1)	
iterator  erase (const_iterator position);
(2)	
size_type erase (const value_type& val);
(3)	
iterator  erase (const_iterator first, const_iterator last);

在这里插入图片描述

void test_set3()
{
	set<int> s;
	s.insert(4);
	s.insert(5);
	s.insert(2);
	s.insert(1);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(1);

	cout << s.erase(3) << endl; // 1 返回删除元素数量
	cout << s.erase(30) << endl; // 0

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl; // 1 2 4 5

	set<int>::iterator pos = s.find(3);
	if (pos != s.end())
		s.erase(pos); // 如果直接删除不存在的元素 会报错 

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl; // 1 2 4 5

	int x;
	while (cin >> x)
	{
		set<int>::iterator pos = s.find(x);
		if (pos != s.end())
		{
			s.erase(pos);
			cout << "删除" << x << "成功" << endl;
		}
		else
		{
			cout << x << "不在set中" << endl;
		}

		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

在这里插入图片描述

count

size_type count (const value_type& val) const;

在这里插入图片描述

count 这个函数其实是为可以存储相同元素的 multiset 准备的。
用count 来判断元素在不在,也比用find会方便一点。

void test_set4()
{
	std::set<int> myset;

	// set some initial values:
	for (int i = 1; i < 5; ++i) myset.insert(i * 3);    // set: 3 6 9 12

	for (int i = 0; i < 10; ++i)
	{
		std::cout << i;
		if (myset.count(i) != 0)
			std::cout << " is an element of myset.\n";
		else
			std::cout << " is not an element of myset.\n";
	}
	cout << endl;
	for (int i = 0; i < 10; ++i)
	{
		std::cout << i;
		if (myset.find(i) != myset.end())
			std::cout << " is an element of myset.\n";
		else
			std::cout << " is not an element of myset.\n";
	}

	/*0 is not an element of myset.
	1 is not an element of myset.
	2 is not an element of myset.
	3 is an element of myset.
	4 is not an element of myset.
	5 is not an element of myset.
	6 is an element of myset.
	7 is not an element of myset.
	8 is not an element of myset.
	9 is an element of myset.

	0 is not an element of myset.
	1 is not an element of myset.
	2 is not an element of myset.
	3 is an element of myset.
	4 is not an element of myset.
	5 is not an element of myset.
	6 is an element of myset.
	7 is not an element of myset.
	8 is not an element of myset.
	9 is an element of myset.*/
}

lower_bound

iterator lower_bound (const value_type& val);
const_iterator lower_bound (const value_type& val) const;

在这里插入图片描述

也就是说返回的是 >= val 的值,没有 val 就返回 val 的下一个。
在这里插入图片描述

void test_set5()
{
	set<int> s;
	s.insert(4);
	s.insert(5);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(7);
	s.insert(9);

	// 返回>= val得位置迭代器  3返回3位置  6返回7位置
	//set<int>::iterator lowIt = s.lower_bound(3); // 存在
	//lowIt = s.lower_bound(6); // 不存在
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	// 要求删除>=x的所有值
	int x;
	cin >> x;
	set<int>::iterator lowIt = s.lower_bound(x);
	s.erase(lowIt, s.end());

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

upper_bound

iterator upper_bound (const value_type& val);
const_iterator upper_bound (const value_type& val) const;

在这里插入图片描述

返回的是 > val 的迭代器位置。
如果都大于,返回的就是 set::end()

void test_set6()
{
	set<int> s;
	s.insert(4);
	s.insert(5);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(7);
	s.insert(9);

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	// 返回>x位置的迭代器  -》 都是返回 7位置的迭代器
	//set<int>::iterator upIt = s.upper_bound(5);  // 存在
	//upIt = s.upper_bound(6); // 不存在

	// 删除x <=  <= y的区间 删除 [x,y]
	int x, y;
	cin >> x >> y;
	auto leftIt = s.lower_bound(x);  // [
	auto rightIt = s.upper_bound(y); // )
	s.erase(leftIt, rightIt);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

multiset

multiset 可以存储重复元素,也是包含在 set 头文件里面。
可以做到排序不去重,接口和 set 基本一致。

void test_set7()
{
	multiset<int> s;
	s.insert(4);
	s.insert(5);
	s.insert(2);
	s.insert(1);
	s.insert(1);
	s.insert(3);
	s.insert(2);
	s.insert(1);
	s.insert(3);
	s.insert(3);
	s.insert(3);
	s.insert(3);

	// 排序 
	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		//*it = 10; /err
		cout << *it << " ";
		++it;
	}
	cout << endl; // 1 1 1 2 2 3 3 3 3 3 4 5

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl; // 1 1 1 2 2 3 3 3 3 3 4 5

	cout << s.count(1) << endl; // 3 返回1的个数
	cout << s.erase(1) << endl; // 3
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl; // 2 2 3 3 3 3 3 4 5

	// 多个x的话,find返回中序第一个x
	// 只有中序第一个3开始 才能遍历所有的3 左根右
	auto pos = s.find(3);
	while (pos != s.end())
	{
		cout << *pos << " ";
		++pos;
	}
	cout << endl; // 3 3 3 3 3 4 5
}

2.map

介绍

在这里插入图片描述

key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map 中的元素是按照 key 来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器。

在这里插入图片描述

在 map 的内部,key 与 value 通过成员类型 value_type 绑定在一起,为其取别名称为 pair 。

make_pair

在这里插入图片描述

make_pair 的方便在于可以自动推导类型。

insert

insert文档

在这里插入图片描述

在这里插入图片描述

插入失败的意思表明这是冗余数据

在这里插入图片描述

void test_map1()
{
	map<string, string> dict;

	// 利用 pair 的构造函数插入数据
	dict.insert(pair<string, string>("sort", "排序")); // 匿名对象
	pair<string, string> kv("insert", "插入");
	dict.insert(kv);

	// make_pair 函数模板
	dict.insert(make_pair("left", "左边"));

	// C++11 才支持
	//dict.insert({ "right", "右边" });

	// 遍历
	//map<string, string>::iterator it = dict.begin();
	auto it = dict.begin();
	while (it != dict.end())
	{
		//cout << *it << " "; // it->operator*()
		//cout << (*it).first << ":" << (*it).second << endl;
		cout << it->first << ":" << it->second << endl; // 省略了一个 ->
		++it;
	}
	cout << endl;

	for (const auto& kv : dict)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}
void test_map2()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto& str : arr)
	{
		map<string, int>::iterator it = countMap.find(str);
		if (it != countMap.end())
		{
			it->second++;
		}
		else
		{
			countMap.insert(make_pair(str, 1));
		}
	}

	for (const auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	//苹果:6
	//西瓜 : 3
	//香蕉 : 2
}

注意 first 是 const对象,不允许修改的。
在这里插入图片描述

利用 erase 返回值的特点,重新统计水果出现次数
ret.first 是迭代器

void test_map2()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

	map<string, int> countMap;
	for (auto& str : arr)
	{
		//pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(str, 1));
		auto ret = countMap.insert(make_pair(str, 1));
		if (ret.second == false)
		{
			ret.first->second++;
			// ret里有一个first和secon
			// first 是指向map的迭代器(指针)
			// secon 是 bool 类型
		}
	}

	for (const auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	//苹果:6
	//西瓜:3
	//香蕉:2
}

在这里插入图片描述

operator[]

文档

mapped_type& operator[] (const key_type& k);
mapped_type& operator[] (key_type&& k);

mapped_tyep 其实就是 value 类型,key_type 就是 key 类型。
在这里插入图片描述

mapped_type& operator[] (const key_type& k)
{
	return (*((this->insert(make_pair(k,mapped_type()))).first)).second
}

// 等价👇
mapped_type& operator[] (const key_type& k)
{
	pair<map<key_type, mapped_type>::iterator, bool> ret = insert(make_pair(k, mapped_type()));
	//return (*(ret.first)).second; // 等价
	return ret.first->second;
}

在这里插入图片描述

利用 operator[] 统计水果次数

void test_map2()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	
	map<string, int> countMap;
	for (auto& str : arr)
	{
		countMap[str]++; // 等价于countMap.operator[](str)++
		// 第一次插入是 k不在map中,默认构造的value是0,然后立刻++变成1了
		// 第二次再插入相同str,value继续++
	}

	for (const auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	//苹果:6
	//西瓜:3
	//香蕉:2
}

同样,也可以利用 operator[] 对字典进行插入、修改操作
在这里插入图片描述

mulitimap

void test_map3()
{
	multimap<string, string> dict;
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("left", "剩余"));
	dict.insert(make_pair("left", "左边")); // 存完全一样的value也是可以的
	dict.insert(make_pair("string", "字符串"));
	dict.insert(make_pair("string", "线"));
	dict.insert(make_pair("string", "细绳"));
	dict.insert(make_pair("string", "一系列事件"));

	for (const auto& kv : dict)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	//left:左边
	//left : 剩余
	//left : 左边
	//string : 字符串
	//string : 线
	//string : 细绳
	//string : 一系列事件
}

在这里插入图片描述

注意,mulitimap没有 operator[] 接口

3.map&set模拟实现

STL库的实现

我们知道,set 你们是 Key,而 map 里面 Key-Value。
如何只用一个 RBTree实现既能兼容 set ,又能兼容 map 呢。
先看看STL标准库是怎么实现的吧!👇
在这里插入图片描述

红黑树节点里面存储的数据类型由 Value 决定,实现了更高维度的泛型。
既然只需要知道 Value 就可以了,那为什么还有传 Key 呢?
在某些接口函数是需要知道 Key 类型的,比如:iterator find(const K& key)中就得知道Key的类型,才能查找,如果都用第一个的 Key 话,map 的第二个参数 pair<K, V> 中的 K 不一定和 key 相同。

data的比较

在insert数据时,我们需要比较data的大小。
但是对于map而已,data的类型是pair<K, V>, pair是支持比较大小,但是pair的比较大小不是我们预期的那样比较Value。
pair文档
在这里插入图片描述

查文档可知,pair 的比较,first小就小,但当 first 不小而 second 小也是小。

而且我们还不能再次重载 pair 的比较运算符,因为 pair 的库里面是重载的了,只是不满足我们要求而已,如果我们再去重载就会变成重定义,编不过。
对于 set 直接用 K 比较就行,而对于 map 就得用 pair 中的first来比较。
利用仿函数。

在这里插入图片描述

归根结底,需要这么拐着弯去比较的原因还是因为C++语言不支持运行时比较参数的类型。如果能支持的话那么,那么我们就可以直接写出 T == Key or T != Key 这样的逻辑了。

迭代器

由于 map、set 中的迭代器都是复用的红黑树中的。
其实迭代器本质上是指针的一个封装的类,是对指针的模拟。
由于标准规定,begin 和 end 必须是前闭后开。
对红黑树进行中序遍历后,可以得到一个有序的序列,因此 begin() 可以指向红黑树中最小节点(即最左侧节点)的位置,end() 指向最大节点(最右侧节点)的下一个位置即 nullptr 。
在这里插入图片描述

RBTree 迭代器中,比较重要的就是 operator++ 和 operator-- 。
++ 时,需要找的就是中序的下一个,按照中序左根右的访问顺序。
假设此时 it 走到5的位置,下一个应该访问6了。
假设此时 it 走到7的位置,右为空,567这个子树也访问完毕了,得去访问8了。
假设此时 it 走到8了,此时应该去访问的是12,也就是右子树的最左节点。
假设此时 it 走到12了,应该访问13,而此时12的右子树为空。
因此可以通过判断当前节点的右子树空与否来决定下一步走向。

  1. 当右子树非空时,右子树的最左节点即是下一个节点
  2. 当右子树为空时,我们需要向上寻找,因为中序是左根右的,所以该子树已经被遍历完了,则应该去找该结点的祖先结点,并且必须得是孩子是父亲的左的祖先节点。
Self& operator++(){ // 左根右
if (_node->_right == nullptr){ // 找孩子是祖先的左的祖先节点

	Node* cur = _node;
	Node* parent = cur->_parent;
	while (parent && parent->_right == cur) { // 如果是右就继续向上
		cur = cur->_parent, parent = parent->_parent;
	}
	_node = parent; // 父亲就是要遍历的下一个节点
}
else { // 非空去找右子树最左节点
	Node* subLeft = _node->_right;
	while (subLeft->_left)
		subLeft = subLeft->_left;
	_node = subLeft;
}
return *this;
}

operator-- 同理,–相当于是倒着遍历树而已,右根左。

  1. 当左子树非空时,左子树的最右节点即是下一个节点
  2. 当左子树为空时,我们需要向上寻找,由右根左的遍历顺序,说明该子树已经被遍历完了,则应该去找该结点的祖先结点,并且必须得是孩子是父亲的右的祖先节点。
Self& operator--() { // 右根左 倒着走
if (_node->_left == nullptr) { // 找孩子是祖先的右的祖先节点
	Node* cur = _node;
	Node* parent = cur->_parent;
	while (parent && parent->_left) {
		cur = cur->_parent, parent = parent->_parent;
	}
	_node = parent;
}
else { // _node->_left != nullptr
	Node* subRight = _node->_left; // 最右节点
	while (subRight->_right)
		subRight = subRight->_right;
	_node = subRight;
}
return *this;
}

迭代器部分源码

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; } // 对应set就是key 对应map就是pair
	Ptr operator->(){ return &_node->_data; }
	Self& operator++(){ // 左根右
		if (_node->_right == nullptr){ // 找孩子是祖先的左的祖先节点

			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur) { // 如果是右就继续向上
				cur = cur->_parent, parent = parent->_parent;
			}
			_node = parent;
		}
		else { // 非空去找右子树最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
				subLeft = subLeft->_left;
			_node = subLeft;
		}
		return *this;
	}
	Self operator++(int) {
		Self tmp(*this);
		++(*this);
		return tmp;
	}
	Self& operator--() { // 右根左 倒着走
		if (_node->_left == nullptr) { // 找孩子是祖先的右的祖先节点
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left) {
				cur = cur->_parent, parent = parent->_parent;
			}
			_node = parent;
		}
		else { // _node->_left != nullptr
			Node* subRight = _node->_left; // 最右节点
			while (subRight->_right)
				subRight = subRight->_right;
			_node = subRight;
		}
		return *this;
	}
	Self operator--(int) {
		Self tmp(*this);
		--(*this);
		return tmp;
	}
	bool operator==(const Self& s) const {
		return _node == s._node;
	}
	bool operator!=(const Self& s) const {
		return _node != s._node;
	}
};

set、map对迭代器的复用。

template<class K>
	class set {
	// ...
	public:
		// 取的是类模板里面的内嵌类型,里面可以带有某些没被实例化的参数
		// 需要用typename 声明这些是类型,等实例化了再去对应的类模板里面取值。
		typedef typename RBTree_for_map_set::RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree_for_map_set::RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin() const { // 这里begin的iterator是const迭代器,
			// 如果不加const,那么调用的是普通版本Begin,返回的又是普通迭代器,
			// const迭代器转化成普通迭代器,编不过,因此得加const,const修饰的this指针,这样就会去调用const版本Begin
			// 而且普通对象也是可以调用const修饰过的begin
			return _t.Begin();
		}
		iterator end() const {
			return _t.End();
		}
	private:
		RBTree_for_map_set::RBTree<K, K, SetKeyOfT> _t;
	};

注意:
1、要加上 typename 声明后面的是类型,实例化之后再去找。
2、 set 中的 key 是不能被修改的,因此无论是 iterator 还是 const_iterator 都是复用的 RBTree 中的const_iterator 。
3、begin、end 需要加上const。

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

RBTree源码

namespace RBTree_for_map_set
{
	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)
			:_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;
		Node* _node;

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

		Ref operator*(){ return _node->_data; } // 对应set就是key 对应map就是pair
		Ptr operator->(){ return &_node->_data; }
		Self& operator++(){ // 左根右
			if (_node->_right == nullptr){ // 找孩子是祖先的左的祖先节点

				Node* cur = _node;
				Node* parent = cur->_parent;
				while (parent && parent->_right == cur) { // 如果是右就继续向上
					cur = cur->_parent, parent = parent->_parent;
				}
				_node = parent;
			}
			else { // 非空去找右子树最左节点
				Node* subLeft = _node->_right;
				while (subLeft->_left)
					subLeft = subLeft->_left;
				_node = subLeft;
			}
			return *this;
		}
		Self operator++(int) {
			Self tmp(*this);
			++(*this);
			return tmp;
		}
		Self& operator--() { // 右根左 倒着走
			if (_node->_left == nullptr) { // 找孩子是祖先的右的祖先节点
				Node* cur = _node;
				Node* parent = cur->_parent;
				while (parent && parent->_left) {
					cur = cur->_parent, parent = parent->_parent;
				}
				_node = parent;
			}
			else { // _node->_left != nullptr
				Node* subRight = _node->_left; // 最右节点
				while (subRight->_right)
					subRight = subRight->_right;
				_node = subRight;
			}
			return *this;
		}
		Self operator--(int) {
			Self tmp(*this);
			--(*this);
			return tmp;
		}
		bool operator==(const Self& s) const {
			return _node == s._node;
		}
		bool operator!=(const Self& s) const {
			return _node != s._node;
		}
	};

	// set RBTree<K, K>
	// map RBTree<K, pair<K, V>>
	// KeyOfT 支持取出T对象中key的仿函数
	template<class K, class T, class KeyOfT> // T决定红黑树存什么类型数据
	class RBTree
	{
		typedef RBTreeNode<T> Node;
	public:
		typedef __RBTreeIterator<T, T&, T*> iterator;
		typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

		iterator Begin() {
			Node* subLeft = _root;
			while (subLeft && subLeft->_left)
				subLeft = subLeft->_left;
			return iterator(subLeft); // RBTree的迭代器用节点指针就可以构造
		}
		iterator End() {
			return iterator(nullptr);
		}
		const_iterator Begin() const {
			Node* subLeft = _root;
			while (subLeft && subLeft->_left)
				subLeft = subLeft->_left;
			return const_iterator(subLeft);
		}
		const_iterator End() const {
			return const_iterator(nullptr);
		}

		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)
			{
				if (kot(cur->_data) < kot(data)) // 取value出来比较
					parent = cur, cur = cur->_right;
				else if (kot(cur->_data) > kot(data))
					parent = cur, cur = cur->_left;
				else
					return make_pair(iterator(cur), true);
			}

			cur = new Node(data);
			Node* newNode = cur; // 保存好新插入的节点
			cur->_col = RED; // 插入黑色一定会破坏规则4,而且规则4很难维护
			if (kot(parent->_data) < kot(data))
				parent->_right = cur;
			else
				parent->_left = cur;
			cur->_parent = parent;
			// 红黑树处理 ...
			while (parent != nullptr && 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 // uncle 不存在或者 存在且为黑  旋转处理
					{
						if (cur == parent->_left) // 单旋
						{
							//     g
							//   p
							// c
							RotateR(grandFather);
							parent->_col = BLACK;
							grandFather->_col = RED;
						}
						else // 双旋
						{
							//     g
							//   p
							//     c
							RotateL(parent);
							RotateR(grandFather);
							cur->_col = BLACK;
							grandFather->_col = RED;
						}
						break; // 旋转完成不需要再处理了
					}
				}
				else // parent == grandFather->_right
				{
					Node* uncle = grandFather->_left;

					if (uncle && uncle->_col == RED) // 情况一 存在且为红
					{
						parent->_col = uncle->_col = BLACK; // 变色
						grandFather->_col = RED;
						cur = grandFather; // 继续向上处理
						parent = cur->_parent;
					}
					else // uncle 不存在或者 存在且为黑  旋转处理
					{
						if (cur == parent->_right) // 单旋
						{
							// g
							//   p
							//     c
							RotateL(grandFather);
							parent->_col = BLACK;
							grandFather->_col = RED;
						}
						else // 双旋
						{
							// g
							//   p
							// c
							RotateR(parent);
							RotateL(grandFather);
							cur->_col = BLACK;
							grandFather->_col = RED;
						}
						break; // 旋转完成不需要再处理了
					}
				}
			}
			_root->_col = BLACK; // 最后要确保根节点为黑色
			return make_pair(iterator(newNode), true);
		}

		iterator Find(const K& key) {
			Node* cur = _root;
			KeyOfT kot;
			while (cur) {
				if (kot(cur->_data) < key)
					cur = cur->_right;
				else if (kot(cur->_data) > key)
					cur = cur->_left;
				else
					return iterator(cur); // 相等时找到了
			}
			return End(); // 找不到
		}
	private:
		void RotateR(Node* parent)
		{
			Node* ppNode = parent->_parent;
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			parent->_left = subLR;
			parent->_parent = subL;
			if (subLR != nullptr)
				subLR->_parent = parent;
			subL->_right = parent;
			if (parent == _root) // parent可能为根节点 旋转后更新root
				_root = subL, _root->_parent = nullptr;
			else
			{
				if (parent == ppNode->_left)
					ppNode->_left = subL;
				else
					ppNode->_right = subL;
				subL->_parent = ppNode;
			}
		}

		void RotateL(Node* parent)
		{
			Node* ppNode = parent->_parent;
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			parent->_right = subRL;
			parent->_parent = subR;
			if (subRL != nullptr) // 30的右子树可能为空
				subRL->_parent = parent;
			subR->_left = parent;
			if (parent == _root) // parent可能为根节点 旋转完成后要更新root
				_root = subR, _root->_parent = nullptr;
			else // 考虑清楚是子树的右还是左 要连接ppNode 和 subR
			{
				if (parent == ppNode->_left)
					ppNode->_left = subR;
				else
					ppNode->_right = subR;
				subR->_parent = ppNode;
			}
		}

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

		int _minHeight(Node* root)
		{
			if (root == nullptr)
				return 0;
			int lh = _minHeight(root->_left);
			int rh = _minHeight(root->_right);
			return lh < rh ? lh + 1 : rh + 1;
		}

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

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

		bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
		{
			// 走到null之后,判断k和black是否相等
			if (nullptr == pRoot)
			{
				if (k != blackCount)
				{
					cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
					return false;
				}
				return true;
			}

			// 统计黑色节点的个数
			if (BLACK == pRoot->_col)
				++k;

			// 检测当前节点与其双亲是否都为红色
			if (RED == pRoot->_col && pRoot->_parent && pRoot->_parent->_col == RED)
			{
				cout << "违反性质三:存在连在一起的红色节点" << endl;
				return false;
			}

			return _IsValidRBTree(pRoot->_left, k, blackCount) &&
				_IsValidRBTree(pRoot->_right, k, blackCount);
		}

		Node* _root = nullptr;

	public:
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
		void Height()
		{
			cout << "最长路径:" << _maxHeight(_root) << endl;
			cout << "最短路径:" << _minHeight(_root) << endl;
		}
		bool IsValidRBTree() // 检查当前红黑树是否满足红黑树的几条规则
		{
			// 空树也是红黑树
			if (nullptr == _root)
				return true;

			// 检测根节点是否满足情况
			if (BLACK != _root->_col)
			{
				cout << "违反红黑树性质二:根节点必须为黑色" << endl;
				return false;
			}

			// 获取任意一条路径中黑色节点的个数 -- 作为比较的基准值
			size_t blackCount = 0;
			Node* pCur = _root;
			while (pCur)
			{
				if (BLACK == pCur->_col)
					blackCount++;

				pCur = pCur->_left; // 取最左路径黑色节点个数作为基准值
			}

			// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
			size_t k = 0;
			return _IsValidRBTree(_root, k, blackCount);
		}
	};
}

set模拟实现

#pragma once
#include "RedBlueTree.h"
namespace yzq {
	template<class K>
	class set {
		struct SetKeyOfT {
			const K& operator()(const K& key) {
				return key;
			}
		};
	public:
		// 取的是类模板里面的内嵌类型,里面可以带有某些没被实例化的参数
		// 需要用typename 声明这些是类型,等实例化了再去对应的类模板里面取值。
		typedef typename RBTree_for_map_set::RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree_for_map_set::RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin() const { // 这里begin的iterator是const迭代器,
			// 如果不加const,那么调用的是普通版本Begin,返回的又是普通迭代器,
			// const迭代器转化成普通迭代器,编不过,因此得加const,const修饰的this指针,这样就会去调用const版本Begin
			// 而且普通对象也是可以调用const修饰过的begin
			return _t.Begin();
		}
		iterator end() const {
			return _t.End();
		}
		pair<iterator, bool> insert(const K& key) {
			pair<typename RBTree_for_map_set::RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
			// auto ret = _t.Insert(key);
			return pair<iterator, bool>(iterator(ret.first._node), ret.second); 
			// node 是__RBTreeIterator 中的对象
		}
		iterator find(const K& key) {
			return _t.Find(key);
		}
	private:
		RBTree_for_map_set::RBTree<K, K, SetKeyOfT> _t;
	};
	void test_set1() {
		set<int> s;
		s.insert(8);
		s.insert(6);
		s.insert(11);
		s.insert(5);
		s.insert(6);
		s.insert(7);
		s.insert(10);
		s.insert(13);
		s.insert(12);
		s.insert(15);
		
		set<int>::iterator it = s.begin();
		while (it != s.end()) {
			cout << *it << " ";
			++it;
		}
		cout << endl;
		for (auto e : s) {
			cout << e << " ";
		}
		cout << endl;
	}
}

map模拟实现

#pragma once
#include "RedBlueTree.h"
namespace yzq {
	template<class K, class V>
	class map {
		struct MapKeyOfT {
			const K& operator()(const pair<K, V>& kv) {
				return kv.first;
			}
		};
	public:
		// 取的是类模板里面的内嵌类型,里面可以带有某些没被实例化的参数
		// 需要用typename 声明这些是类型,等实例化了再去对应的类模板里面取值。
		typedef typename RBTree_for_map_set::RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree_for_map_set::RBTree<K, pair<K, V>, MapKeyOfT>::const_iterator const_iterator;

		iterator begin() {
			return _t.Begin();
		}
		iterator end() {
			return _t.End();
		}
		pair<iterator, bool> insert(const pair<K, V>& kv) {
			return _t.Insert(kv);
		}
		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_for_map_set::RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
	void test_map1() {
		map<string, int> m;
		m.insert(make_pair("111", 1));
		m.insert(make_pair("555", 5));
		m.insert(make_pair("333", 3));
		m.insert(make_pair("222", 2));
		m.insert(make_pair("999", 9));

		map<string, int>::iterator it = m.begin();
		while (it != m.end()) {
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
		for (auto kv : m) {
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}

	void test_map2()
	{
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		map<string, int> countMap;
		for (auto& str : arr)
			countMap[str]++;

		for (const auto& kv : countMap)
			cout << kv.first << ":" << kv.second << endl;
		//苹果:6
		//西瓜 : 3
		//香蕉 : 2
	}

	void test_map3()
	{
		map<string, string> dict;
		dict["insert"];
		dict["insert"] = "";
		dict["left"] = "";
	}
}

尾声

🌹🌹🌹

写文不易,如果有帮助烦请点个赞~ 👍👍👍

Thanks♪(・ω・)ノ🌹🌹🌹

😘😘😘

👀👀由于笔者水平有限,在今后的博文中难免会出现错误之处,本人非常希望您如果发现错误,恳请留言批评斧正,希望和大家一起学习,一起进步ヽ( ̄ω ̄( ̄ω ̄〃)ゝ,期待您的留言评论。
附GitHub仓库链接

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值