# map
不使用 `multimap`。
## 介绍
`map` 是包含键值对 `<key, value>` 的有序映射关联容器,可以看做是 `key` 映射到 `value`。`map` 要求所有的映射的 `key` 是互不相同的(唯一的),而 `value` 可以重复。
`map` 的底层是红黑树。所有映射按照 `key` 排序,并较为快速地兼顾了查询、插入、删除操作。
使用 `map` 需要包含头文件 `<map>`。
定义:`map<key 类型, value 类型, 比较器> 容器名`(还有一个模板参数,不提及),比较器可以不传入。默认比较器为 `<` 规则。
## 成员函数
迭代器相关内容请参考 [迭代器](迭代器.md)。
`map` 的插入、删除、查找操作都是 $O(\log N)$ 的,其中$N$ 为映射对数量。
定义一个 `map<int, int> m, p;`,以下操作的时间复杂度均为 $O(\log N)$。
- `m[x]`,返回 `x` 映射到的 `value` 的引用。如果 `m` 中不存在 `key` 为 `x` 的映射,会新建一个 `x` 到 `int `类型默认值 $0$ 的映射 `<x, 0>`。
- 如果 `value` 类型为 `string`,则新建了一个 `<x, "">` 的映射对。
- 对于 `value` 类型的其他情况,以此类推。
- 你可以将 `map` 容器视为一个“万能”的数组!
- `m[x] = y`,将 `x` 映射到的 `value` 修改为 `y`。如果 `m` 中不存在 `key` 为 $x$ 的映射,则新建一个映射 `<x, y>`。
- 也可以用 `m.insert({x, y})`,这里不在详细介绍 `.insert()` 的用法。
- `m.count(x)`,返回 `m` 中有多少个 `key` 为 `x` 的映射,即判断是否存在 `key` 为 `x` 的映射。
- `m.erase(x)`,删除 `key` 为 `x` 的映射,如果不存在 `key` 为 `x` 的映射则不删除。
- `m.erase(it)`,删除 `it` 迭代器指向的映射,要求 `it` 不能为 `m` 的尾迭代器 `m.end()`。
- `m.find(x)`,返回 `key` 为 `x` 的映射的迭代器,如果不存在 `key` 为 `x` 的映射则返回 `m` 的尾迭代器 `m.end()`。
- 查找映射、判断映射是否存在应使用 `.count()` 或者 `.find()` 而非 `[]`,这样可以加快效率。
- `m.lower_bound(x), m.upper_bound(x)`,返回第一个 `key` 大于等于和大于 `x` 的映射的迭代器。
以下操作的时间复杂度均为 $O(1)$
- `m.begin(), m.end()`,返回指向 `m` 的头尾迭代器。
- `m.empty()`,返回 `m` 是否为空。
- `m.size()`,返回 `m` 所包含的映射对数量。
- `m.clear()`,其中容器 `m`。
- `m.swap(p)`,交换容器 `m` 和 `p`,也可以使用代码 `swap(m, p)`。
### 基本使用
```cpp
map<int, int> m;
map<int, int> h[100]; // 定义 100 个 map 容器
m[1] = 5; // [(1, 5)]
m[3] = 14; // [(1, 5); (3, 14)]
m[2] = 7; // [(1, 5); (2, 7); (3, 14)]
m[0] = -1; // [(0, -1); (1, 5); (2, 7); (3, 14)]
m.erase(2); // [(0, -1); (1, 5); (3, 14)]
cout << m[1] << endl; // 5
cout << m.count(7) << endl; // 0
cout << m.count(1) << endl; // 1
cout << m[2] << endl; // 0,若不存在映射则新建一个映射 (2, 0)
```
## 遍历容器
`map` 中存储映射的方式是使用 C++ `pair`。如果你定义了一个 `map<string, int> m`,那么所有的映射都是类型 `pair<string, int>`,使用 `.first` 和 `.second` 可以取出 `pair` 中的第 $1, 2$ 个元素。
可以使用如下方式遍历:
```cpp
for (const auto &x : m) { // 常引用
cout << x.first << " " << x.second << endl;
}
for (auto x : m) { // 将容器 m 中的每个映射依次拷贝给 x
cout << x.first << " " << x.second << endl;
}
for (pair<string, int> x : m) { // 同上
cout << x.first << " " << x.second << endl;
}
// .first 是第一个元素 string 类型,.second 是第二个元素 int 类型
```
当你只需要遍历容器而不涉及到修改、删除操作时,推荐使用第一种遍历方法。需要注意到常引用和拷贝的区别。
## 在遍历时进行修改和删除
由于在遍历时可以使用引用,你可以用下述方式修改映射中的 `value`:
```cpp
for (auto &x : m) {
x.second = 1234; // 把所有映射的 value 改为 1234
}
```
```cpp
map<string, int> h = {{"zsc", 250}};
auto it = h.find("zsc"); // it 为指向 key 为 "zsc" 的迭代器
it->second = 0; // 将 "zsc" 映射的 `value` 修改为 0
```
你可以在遍历时修改映射的 `value`,但不能增加新的映射、删除已有映射,否则会 RE。以下代码是一个错误的示例。
```cpp
map<int, int> m;
for (int i = 0; i < 10; ++i) {
m[i] = i;
}
for (auto &it : m) {
cout << "Current Key: " << it.first << endl;
m.erase(it.first);
}
```
因此你要删除时,你可以拷贝一个新 `map` 来删除,或者将需要删除的 `key` 记录下来最后删除。
### 拷贝新 map
```cpp
map<int, int> m, M;
for (int i = 0; i < 10; ++i) {
m[i] = i;
}
int current_iteration = 0;
for (const auto &it : m) {
if (current_iteration % 3 == 0) {
M[it.first] = it.second;
}
current_iteration++;
}
swap(m, M);
cout << "映射: " << endl;
for (const auto &it : m) {
cout << it.first << " " << it.second << endl;
}
/*
* 映射:
* 0 0
* 3 3
* 6 6
* 9 9
*/
```
### 记录后删除
```cpp
map<int, int> m;
for (int i = 0; i < 10; ++i) {
m[i] = i;
}
vector<int> to_erase;
int current_iteration = 0;
for (const auto &it : m) {
if (current_iteration % 3 == 0) {
to_erase.push_back(it.first);
}
current_iteration++;
}
for (int key : to_erase) {
m.erase(key);
}
cout << "剩余的映射:" << endl;
for (const auto &it : m) {
cout << it.first << " " << it.second << endl;
}
/*
* 剩余的映射:
* 1 1
* 2 2
* 4 4
* 5 5
* 7 7
* 8 8
*/
```
## 比较器
请参考 [自定义比较器](自定义比较器.md)。
比较器的细节同 `set`。
## lower_bound, upper_bound
```cpp
map<int, int> m;
m[3] = 5; // [(3, 5)]
m[11] = 4; // [(3, 5); (11, 4)]
m[10] = 491; // [(3, 5); (10, 491); (11, 4)]
cout << m.lower_bound(10)->first << " " << m.lower_bound(10)->second
<< '\n'; // 10 491
cout << m.upper_bound(10)->first << " " << m.upper_bound(10)->second
<< '\n'; // 11 4
m.erase(11); // [(3, 5); (10, 491)]
if (m.upper_bound(10) == m.end()) {
cout << "end" << endl; // Prints end
}
```
## 参考资料
[关联式容器](https://oi-wiki.org/lang/csl/associative-container/)。