目录
[1. 基本介绍](#1. 基本介绍)
[2. set 的使用](#2. set 的使用)
[3. multiset 与 set](#3. multiset 与 set)
[1. 基本介绍](#1. 基本介绍)
[2. pair的使用](#2. pair的使用)
[3. map 的使用](#3. map 的使用)
[(6)★operator[ ]([ ]重载 )★](#(6)★operator[ ]([ ]重载 )★)
[4. multimap 与 map](#4. multimap 与 map)
前言
上篇文章我们讲解了二叉搜索树的一些认识,而本篇文章我们将要对C++中的两个容器 set 和 map进行介绍,掌握它们两个的基本使用。 set 和 map 的底层其实都是一种搜索树,但不是普通的二叉搜索树,而是红黑树(红黑树是在普通二叉搜索树上进行了一些优化了的搜索树),在认识set和map时我们只需要知道它们的通过搜索树实现的就可以了(以上篇文章讲解的普通二叉搜索树来理解当然就够了)。
一、set
set介绍的文档链接:set - C++ Reference
1. 基本介绍
对于set,我们可以将它当做二叉搜索树的一种key模型来看待。set是通过封装红黑树实现的。
set的底层是一棵二叉搜索树(红黑树),所以说在set在存储的数据通过中序遍历就是有序的。可以用来排序。
并且set中的数据不能重复,插入已有的数据会失败,因此 set 常常会用来帮助我们对数据去重。
在set中的元素不能在容器中修改(set中的元素总是const的),但是我们可以从容器中插入或删除它们。
2. set 的使用
(1)初始化
和其他容器一样,库里面的set也是通过模板实现的,如图所示:
其中模板参数的意思如下:
T: set中存放元素的类型(实际在底层存储的一个键值对,即这里的T就是<Key, Key>,当下可以不用理解太深)。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
所以,一般我们在使用时,只需要传递一个存储数据的类型就可以了,即:
cpp
#include<set>
using namespace std;
int main()
{
set<int> MySet; // 定义一个set对象
return 0;
}
当我们构造一个set对象时,也可以通过其他方式,如图是库中的构造函数:
这三者方式都比较常见。
(2)插入&删除&查找
对于插入
如图所示:
其中第2、3个都比较好理解。
对于第1个函数的意思是:
在set中插入元素val,实际插入的是 <val, val> 构成的键值对,
如果插入成功,返回 <该元素在set中的位置(迭代器),true> ;
如果插入失败,说明 val 在set中已经 存在,返回 <在set中的位置(迭代器),false>
对于删除
如图所示:
第1个的意思:删除set中迭代器position所指位置的元素。
第2个的意思:删除set中值为x的元素,返回删除的元素的个数
第3个的意思:删除set中[first, last)区间中的元素
对于查找
如图所示:
就是查找val,找到后,返回其对应的迭代器;否则,就返回 set::end 。
(3)迭代器
set中可以通过迭代器进行遍历,如图所示:
通过set的迭代器遍历出的元素,是有序的,如以下代码:
cpp
#include<iostream>
#include<set>
using namespace std;
int main()
{
int arr[] = { 2,4,5,6,3,1,7,8,9,10 };
set<int> MySet(arr,arr + sizeof(arr) / sizeof(arr[0])); //使用数组一个set对象
set<int>::iterator it = MySet.begin();
while (it != MySet.end())
{
cout << *it << " "; //输出:1 2 3 4 5 6 7 8 9 10
it++;
}
cout << endl;
//反向迭代器
set<int>::reverse_iterator rit = MySet.rbegin();
while (rit != MySet.rend())
{
cout << *rit << " "; //输出:10 9 8 7 6 5 4 3 2 1
rit++;
}
cout << endl;
return 0;
}
(4)其他常见操作
对于count,如图:
作用:返回set中值为val的元素的个数。
一般这个在set中并不起作用,因为在set中的数据都是唯一的,所以在set中 count函数的作用就是查找,如果存在val,就返回1;不存在就返回0。由于set中有find这个查找的函数,所以一般这个函数我们可以认识在set中没有意义。但是在 multiset 就是有意义的。
对于lower_bound,如图:它的作用:返回一个指向第一个不小于给定值 val 的元素的迭代器。
对于upper_bound,如图:它的作用:返回一个指向第一个大于给定值 val 的元素的迭代器。
对于clear,如图:作用是将set中的所有元素清空
还有两个,如图:empty用来判空
size可以返回set中有效元素的个数
以上就是set的一些常见使用,其他还有一些不常见的,可以才参考set文档::set - C++ Reference了解。
3. multiset 与 set
multiset介绍的文档链接:multiset - C++ Reference
multiset和set都在同一个头文件中<set>中:
multiset的内容和set大部分都是相同的;
与set的区别就是,multiset中存储的元素可以重复,而set是中元素是唯一的。
如以下代码所示:
cpp
#include<iostream>
#include<set>
using namespace std;
int main()
{
int arr[] = { 2,4,5,6,3,3,3,3,3,3,4,4,4,1,7,8,9,10 };
//set输出
set<int> MySet(arr, arr + sizeof(arr) / sizeof(arr[0]));
for (auto& e : MySet)
{
cout << e << " "; //输出:1 2 3 4 5 6 7 8 9 10
}
cout << endl;
//multiset输出
multiset<int> MyMultset(arr, arr + sizeof(arr) / sizeof(arr[0]));
for(auto& e: MyMultset)
{
cout << e << " "; //输出:1 2 3 3 3 3 3 3 4 4 4 4 5 6 7 8 9 10
}
cout << endl;
return 0;
}
二、map
map文档介绍链接:map - C++ Reference
1. 基本介绍
map也是通过封装二叉搜索树(红黑树)实现的。
map相当于二叉搜索树中的Key_Value模型,在map的二叉树中,每一个结点存储的是由键值key和值value组合而成的元素。这个元素是一个pair的对象。
在内部,map中的元素是按照键值key进行比较排序的。
在map中,每一个结点的key与value是绑定在一起,其中key仍然不能被修改(key是const的),而value则可以被修改。
2. pair的使用
在map中每一个节点存储的都是key和value组合的pair对象。下面我们来简单介绍一个pair。
pair的文档链接:pair - C++ Reference
如图:
pair类可以将一对值组合在一起,这两个值可以是不同的类型(T1和 T2)。它有两个公有成员 first 和 second 。first对应T1,second对应T2。
pair的构造函数:
可以构造空的pair,也可以拷贝构造,还可以通过传递两个数据(first_type& a, second_type& b)进行构造。
我们还有一种方法来实现一个pair:
使用make_pair来构造pair。通过传递两个参数给make_pair来返回一个pair也是常见的方法。
3. map 的使用
(1)初始化
如图所示:
这是map的模板参数列表,与set相比map就有两个数据类型。
key: 对应键值对中key的类型
T: 对应键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比 较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户 自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:空间配置器。
所以,map的定义就如下:
cpp
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<int, int> MyMap; //定义一个map的对象
return 0;
}
再看map中的一些变量重定义:
从这里就可以看出来map中存储的就是pair<Key,T>。
map的构造函数:
(2)★插入★
对于插入
如图所示:

首先来看第一个,我们插入的val必须是一个pair对象(因为这里的value_type就是pair重定义而来),所以我们的插入必须要调用pair的构造或者是make_pair函数。这里我们有多种方法,如下所示:
cpp
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
map<string, string> MyMap;
pair<string, string> kv1("insert", "插入");
MyMap.insert(kv1);
MyMap.insert(pair<string, string>("sort", "排序"));//只有转换为pair才行
// C++98 隐式类型转换:const char* -> string
MyMap.insert(make_pair("string", "字符串"));
// C++11 多参数的构造函数隐式类型转换:统一的初始化语法 { }
MyMap.insert({ "int", "整型" });
return 0;
}
对于返回值,在map中插入键值对val后,如果插入成功,则返回的pair的first是指向新插入的元素的迭代器,返回值是true;如果插入失败(即元素已经存在),则返回的pair的first是指向已存在这个的元素的迭代器,返回值是false;
然后,对于第二个构造函数在传递val的时候也需要注意要传pair类型的数据。
最后,第三个的迭代器构造,需要注意传递的迭代器是一个指向pair类型的迭代器。
(3)删除
map的删除比较简单,如图:
第1个函数:删除position位置上的元素
第2个函数:删除键值为k的元素,即删除map中pair中first为k的这个pair类型的元素。
第3个函数:删除[first, last)区间中的元素
(4)查找
如图所示:

map的find查找是按照pair中的first进行查找的,将k与pair中的first比较进行查找。找到了就返回该元素所在的迭代器;否则就返回map::end。
(5)迭代器
map迭代器也比较全面,如图所示:
使用也比较简单,唯一需要注意的就是解引用访问时访问的pair类型,而我们需要访问的是pair中的first和second这里个成员变量。这里我们简单使用一下:
cpp
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
map<string, string> MyMap;
MyMap.insert(make_pair("string", "字符串"));
MyMap.insert(make_pair("int", "整型"));
MyMap.insert(make_pair("insert", "插入"));
MyMap.insert(make_pair("erase", "删除"));
MyMap.insert(make_pair("sort", "排序"));
//访问
map<string, string>::iterator it = MyMap.begin();
while (it != MyMap.end())
{
//两种方式都可以
//cout << (*it).first << "-->" << (*it).second << endl; //直接解引用访问
//使用->重载访问,编译器做了优化,本来是(it->)->first,两个->优化为了一个
cout << it->first << "-->" << it->second << endl;
it++;
}
cout << endl;
return 0;
}
(6)★operator[ ]([ ]重载 )★
在map中使用比较复杂的就是operator[ ]这个函数了。它既有插入,也有查找的意思。
首先来看这个函数:
相当于调用:

可以发现这是很复杂的,所以我们先把它分开写,让逻辑清晰一下:

所以,这里的operator[ ] 的功能其实就是:
先通过在map中插入一个 k (键对值的第一个变量类型)。
- 如果k已经在树里面了,这时的ret就是pair<树里面的k所在节点的迭代器iterator, false>;
- 如果k不在树里面,这时的ret就是pair<新插入k所在节点的迭代器iterator, false>;
然后再通过ret得到k所在的迭代器,通过对这个迭代器解引用(或者是使用->)就可以找到map中k对应的value,然后在返回,拿到k所在的value,由于这里是传引用返回,那么通过这个operator[ ] 也就可以直接修改map中k对应的value值了。
在一些地方使用 [ ] 也是非常方便的。
使用示例:

图中这两种方法的实现效果都是一样的,运行结果:

(7)其他常见操作
map中也还有许多其他的函数接口,比如:
|------------------------------------------------------------------------|---------------------------------------------|--------------------------------------------------------------------------------|
| 接口 | 函数声明 | 作用 |
| empty | bool empty() const; | 检测map中的元素是否为空 |
| size | size_type size() const; | 返回map中有效元素的个数 |
| swap | void swap (map& x); | 交换两个map中的所有元素 |
| clear | void clear(); | 将map中的元素清空 |
| count | size_type count (const key_type& k) const; | 返key为k的键值在map中的个数,注意 map中key是唯一的,因此该函数的返回值 要么为0,要么为1,因此也可以用该函数来 检测一个key是否在map中 |
以上便是对map的常见接口的认识了。
4. multimap 与 map
multimap介绍的文档链接:multimap - C++ Reference
multimap和map都在头文件<map>中:
multimap和map的接口功能几乎是一样的。
不同的是multimap的多个键值对pair之间的key(即map存储的pair中的first)是可以重复的。而map的不同pair之间的key不能重复。也就是说,在multimap中,key是可以有多个的,而相同的key对应的value是可能不同的。
另外还有一个点:multimap的count函数是可以发挥实际作用的。
感谢各位观看!希望能多多支持!







