目录
[1.1 序列式容器](#1.1 序列式容器)
[1.2 关联式容器](#1.2 关联式容器)
[1.3 map 和 set 底层结构](#1.3 map 和 set 底层结构)
[二、set 的使用](#二、set 的使用)
[2.1 set 的特点](#2.1 set 的特点)
[2.2 set 常用操作](#2.2 set 常用操作)
[(6) lower_bound和upper_bound](#(6) lower_bound和upper_bound)
[2.3 multiset](#2.3 multiset)
[三、map 的使用](#三、map 的使用)
[3.1 map 的特点](#3.1 map 的特点)
[3.2 map 基本结构](#3.2 map 基本结构)
[3.3 pair对象](#3.3 pair对象)
[3.4 插入/初始化](#3.4 插入/初始化)
[3.5 访问与修改](#3.5 访问与修改)
[(1)通过 find](#(1)通过 find)
[(2)通过 []](#(2)通过 [])
[3.6 删除](#3.6 删除)
[3.7 遍历](#3.7 遍历)
[3.8 multimap vs map](#3.8 multimap vs map)
一、序列式容器与关联式容器
1.1 序列式容器
STL 中常见的 string、vector、list、deque、array、forward_list 等属于序列式容器。
特点:
逻辑结构是线性序列
元素之间关系不强
主要按"位置"访问
可以随意交换元素位置,不影响整体结构
本质:按顺序存储数据
1.2 关联式容器
关联式容器包括:
-
set / multiset -
map / multimap -
unordered_set / unordered_map
特点:
逻辑结构是非线性(通常是树结构)
元素之间存在"键值关系"
按 key 进行存储和查找
不能随意交换位置,否则结构被破坏
本质:按关键字组织数据
1.3 map 和 set 底层结构
底层实现:红黑树(平衡二叉搜索树)
时间复杂度:O(logN)
遍历方式:中序遍历(天然有序)
二、set 的使用
2.1 set 的特点
自动去重
自动排序(默认升序)
key 即 value
不允许修改元素值
2.2 set 常用操作
(1)插入
cpp
set<int> s;
s.insert(10);
s.insert(20);
(2)遍历
cpp
for (auto e : s)
{
cout << e << " ";
}
(3)查找
cpp
if (s.find(10) != s.end())
{
cout << "存在";
}
返回10这个值的迭代器
(4)删除
cpp
s.erase(10); // 按值删除
s.erase(s.begin()); // 按迭代器删除
这里也存在迭代器失效的问题,所以用vs去访问的话会报错
(5)计数
cpp
cout << s.count(10);
返回10的个数,一般适用于multiset
(6) lower_bound和upper_bound
cpp
#include<set>
#include<iostream>
using namespace std;
int main()
{
std::set<int> myset;
for (int i = 1; i < 10; i++)
myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
// [30, 50]值
// [25, 55]值
//// 返回 >= 30
auto itlow = myset.lower_bound(30);
//// 返回 > 50
auto itup = myset.upper_bound(50);
// 返回 >= 25
//auto itlow = myset.lower_bound(25);
// 返回 > 55
//auto itup = myset.upper_bound(55);
// 删除这段区间的值
myset.erase(itlow, itup);
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
}
这里删除传入的迭代器都是左闭右开,所以要删除一段区间,左迭代器可以用lower_bound得到,它可以返回>=某个值的迭代器,右迭代器可以传upper_bound,它返回>某个值的迭代器

2.3 multiset
特点:
允许重复元素
仍然有序
区别:
| 容器 | 是否去重 |
|---|---|
| set | 去重 |
| multiset | 不去重 |
三、map 的使用
3.1 map 的特点
key-value 结构
key 不可重复
自动按 key 排序
支持修改 value
3.2 map 基本结构
cpp
map<Key, value>
例如:
map<string, int> m;
3.3 pair对象
pair是一个类模板

里面有2个成员变量

一个是first,一个是second
pair常被用于map,作参数,作返回值
first相当于key,second相当于value,我们可以通过pair来对key/value进行操作
3.4 插入/初始化

cpp
pair<string, string> kv1("first", "第一个");
map<string, string> dict = {kv1, pair<string, string>("second", "第二个")};
map<string, string> dict = { {"left", "左边"}, {"right", "右边"}, {"insert", "插入"},{ "string", "字符串" } };
这里value_type 就是pair,map的初始化支持传入一个pair对象,可以是有名对象,也可以是匿名对象,或者直接写成传一个initializer_list的对象,c++11支持多参数的隐式类型转换,里面的花括号生成一个pair对象,所有pair再转换成一个initializer_list的对象


cpp
pair<string, string> kv1("first", "第一个");
dict.insert(kv1);
dict.insert(pair<string, string>("second", "第二个"));
dict.insert(make_pair("sort", "排序"));
// C++11
dict.insert({ "auto", "自动的" });
这里插入,可以传一个pair对象,可以传一个函数模板make_pair

他会返回一个pair对象,并且可以自动推导类型,日常使用也是非常常用的
cpp
dict.insert({ "auto", "自动的" });
// 插入时只看key,value不相等不会更新
dict.insert({ "auto", "自动的xxxx" });
插入不会自动更新

3.5 访问与修改
(1)通过 find
auto it = m.find("apple");
if (it != m.end())
{
it->second++;
}
这里的second是一个pair对象的一个成员变量
(2)通过 []
m["apple"] = 10;
m["banana"]++;
特点:
key 不存在 → 自动插入
key 存在 → 修改 value
它跟insert的功能是相似的,所以要确保key存在再用,不然就又会插入一个不存在的key,谨慎使用
3.6 删除
m.erase("apple");
m.erase(it);
3.7 遍历
for (auto& e : m)
{
cout << e.first << ":" << e.second;
}
3.8 multimap vs map
| 容器 | 是否允许重复 key |
|---|---|
| map | 不允许 |
| multimap | 允许 |
区别:
multimap 不能使用 []
find 可能返回多个 key 中第一个