- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本 - 添加 C++ Primer 练习代码 - 新增 Learn C++ 教程的 C++ 开发简介章节 - 添加头文件解析文档 - 更新 mkdocs.yml,包含新教程的目录结构 - 修改项目设置,使用 Python 3.10环境
145 lines
7.0 KiB
Markdown
145 lines
7.0 KiB
Markdown
# 第十一章 关联容器
|
||
|
||
- 关联容器和顺序容器的不同:关联容器中的元素时按照**关键字**来保存和访问的。
|
||
- 关联容器支持通过关键字来高效地查找和读取元素,基本的关联容器类型是 `map`和 `set`。
|
||
|
||
**关联容器类型**:
|
||
|
||
| 容器类型 | 解释 |
|
||
|-----|-----|
|
||
| 按顺序存储 | |
|
||
| `map` | 关键数组:保存`关键字-值`对 |
|
||
| `set` | 关键字即值,即只保存关键字的容器 |
|
||
| `multimap` | 支持同一个键多次出现的`map` |
|
||
| `multiset` | 支持同一个键多次出现的`set` |
|
||
| 无序集合 | |
|
||
| `unordered_map` | 用哈希函数组织的`map` |
|
||
| `unordered_set` | 用哈希函数组织的`set` |
|
||
| `unordered_multimap` | 哈希组织的`map`,关键字可以重复出现 |
|
||
| `unordered_multiset` | 哈希组织的`set`,关键字可以重复出现 |
|
||
|
||
## 关联容器概述
|
||
|
||
### 定义关联容器
|
||
|
||
- 需要指定元素类型。
|
||
- 列表初始化:
|
||
- `map`:`map<string, int> word_count = {{"a", 1}, {"b", 2}};`
|
||
- `set`:`set<string> exclude = {"the", "a"};`
|
||
|
||
### 关键字类型的要求
|
||
|
||
- 对于有序容器,关键字类型必须定义元素比较的方法。默认是`<`。
|
||
- 如果想传递一个比较的函数,可以这样定义:`multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);`
|
||
|
||
### pair
|
||
|
||
- 在`utility`头文件中定义。
|
||
- 一个`pair`保存两个数据成员,两个类型不要求一样。
|
||
|
||
**pair的操作**:
|
||
|
||
| 操作 | 解释 |
|
||
|-----|-----|
|
||
| `pair<T1, T2> p;` | `p`是一个`pair`,两个类型分别是`T1`和`T2`的成员都进行了值初始化。 |
|
||
| `pair<T1, T2> p(v1, v2);` | `first`和`second`分别用`v1`和`v2`进行初始化。 |
|
||
| `pair<T1, T2>p = {v1, v2};` | 等价于`p(v1, v2) |
|
||
| `make_pair(v1, v2);` | `pair`的类型从`v1`和`v2`的类型推断出来。 |
|
||
| `p.first` | 返回`p`的名为`first`的数据成员。 |
|
||
| `p.second` | 返回`p`的名为`second`的数据成员。 |
|
||
| `p1 relop p2` | 运算关系符按字典序定义。 |
|
||
| `p1 == p2` | 必须两对元素两两相等 |
|
||
| `p1 != p2` | 同上 |
|
||
|
||
## 关联容器操作
|
||
|
||
**关联容器额外的类型别名**:
|
||
|
||
| 类型别名 | 解释 |
|
||
|-----|-----|
|
||
| `key_type` | 此容器类型的关键字类型 |
|
||
| `mapped_type` | 每个关键字关联的类型,只适用于`map` |
|
||
| `value_type` | 对于`map`,是`pair<const key_type, mapped_type>`; 对于`set`,和`key_type`相同。 |
|
||
|
||
### 关联容器迭代器
|
||
|
||
- 解引用一个关联容器迭代器时,会得到一个类型为容器的`value_type`的值的引用。
|
||
- `set`的迭代器是`const`的。
|
||
- 遍历关联容器:使用`begin`和`end`,遍历`map`、`multimap`、`set`、`multiset`时,迭代器按**关键字升序**遍历元素。
|
||
|
||
### 添加元素
|
||
|
||
**关联容器`insert`操作**:
|
||
|
||
| `insert`操作 | 关联容器 |
|
||
|-----|-----|
|
||
| `c.insert(v)` `c.emplace(args)` | `v`是`value_type`类型的对象;`args`用来构造一个元素。 对于`map`和`set`,只有元素的关键字不存在`c`中才插入或构造元素。函数返回一个`pair`,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的`bool`值。对于`multimap`和`multiset`则会插入范围中的每个元素。|
|
||
| `c.insert(b, e)` `c.insert(il)` | `b`和`e`是迭代器,表示一个`c::value_type`类型值的范围;`il`是这种值的花括号列表。函数返回`void`。对于 `map`和`set`,只插入关键字不在`c`中的元素。 |
|
||
| `c.insert(p, v)` `c.emplace(p, args)` | 类似`insert(v)`,但将迭代器`p`作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素。 |
|
||
|
||
向`map`添加元素:
|
||
- `word_count.insert({word, 1});`
|
||
- `word_count.insert(make_pair(word, 1));`
|
||
- `word_count.insert(pair<string, size_t>(word, 1));`
|
||
- `word_count.insert(map<string, size_t>::value_type (word, 1));`
|
||
|
||
### 删除元素
|
||
|
||
**从关联容器中删除元素**:
|
||
|
||
| 操作 | 解释 |
|
||
|-----|-----|
|
||
| `c.erase(k)` | 从`c`中删除每个关键字为`k`的元素。返回一个`size_type`值,指出删除的元素的数量。 |
|
||
| `c.erase(p)` | 从`c`中删除迭代器`p`指定的元素。`p`必须指向`c`中一个真实元素,不能等于`c.end()`。返回一个指向`p`之后元素的迭代器,若`p`指向`c`中的尾元素,则返回`c.end()` |
|
||
| `c.erase(b, e)` | 删除迭代器对`b`和`e`所表示范围中的元素。返回`e`。 |
|
||
|
||
### 下标操作
|
||
|
||
**`map`和`unordered_map`的下标操作**:
|
||
|
||
| 操作 | 解释 |
|
||
|-----|-----|
|
||
| `c[k]` | 返回关键字为`k`的元素;如果`k`不在`c`中,添加一个关键字为`k`的元素,对其值初始化。 |
|
||
| `c.at(k)` | 访问关键字为`k`的元素,带参数检查;若`k`不存在在`c`中,抛出一个`out_of_range`异常。 |
|
||
|
||
### 查找元素
|
||
|
||
**在一个关联容器中查找元素**:
|
||
|
||
| 操作 | 解释 |
|
||
|-----|-----|
|
||
| `c.find(k)` | 返回一个迭代器,指向第一个关键字为`k`的元素,若`k`不在容器中,则返回尾后迭代器 |
|
||
| `c.count(k)` | 返回关键字等于`k`的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1。 |
|
||
| `c.lower_bound(k)` | 返回一个迭代器,指向第一个关键字**不小于**`k`的元素。 |
|
||
| `c.upper_bound(k)` | 返回一个迭代器,指向第一个关键字**大于**`k`的元素。 |
|
||
| `c.equal_range(k)` | 返回一个迭代器`pair`,表示关键字等于`k`的元素的范围。若`k`不存在,`pair`的两个成员均等于`c.end()`。 |
|
||
|
||
- `lower_bound`和`upper_bound`不适用于无序容器。
|
||
- 下标和`at`操作只适用于非`const`的`map`和`unordered_map`。
|
||
|
||
## 无序容器
|
||
|
||
- 有序容器使用比较运算符来组织元素;无序容器使用哈希函数和关键字类型的`==`运算符。
|
||
- 理论上哈希技术可以获得更好的性能。
|
||
- 无序容器在存储上组织为一组桶(bucket),每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。
|
||
|
||
**无序容器管理操作**:
|
||
|
||
| 操作 | 解释 |
|
||
|-----|-----|
|
||
| **桶接口** | |
|
||
| `c.bucket_count()` | 正在使用的桶的数目 |
|
||
| `c.max_bucket_count()` | 容器能容纳的最多的桶的数目 |
|
||
| `c.bucket_size(n)` | 第`n`个桶中有多少个元素 |
|
||
| `c.bucket(k)` | 关键字为`k`的元素在哪个桶中 |
|
||
| **桶迭代** | |
|
||
| `local_iterator` | 可以用来访问桶中元素的迭代器类型 |
|
||
| `const_local_iterator` | 桶迭代器的`const`版本 |
|
||
| `c.begin(n)`,`c.end(n)` | 桶`n`的首元素迭代器 |
|
||
| `c.cbegin(n)`,`c.cend(n)` | 与前两个函数类似,但返回`const_local_iterator`。 |
|
||
| **哈希策略** | |
|
||
| `c.load_factor()` | 每个桶的平均元素数量,返回`float`值。 |
|
||
| `c.max_load_factor()` | `c`试图维护的平均比桶大小,返回`float`值。`c`会在需要时添加新的桶,以使得`load_factor<=max_load_factor` |
|
||
| `c.rehash(n)` | 重组存储,使得`bucket_count>=n`,且`bucket_count>size/max_load_factor` |
|
||
| `c.reverse(n)` | 重组存储,使得`c`可以保存`n`个元素且不必`rehash`。 |
|