# 【5-4】排序關聯式容器 — map & multimap
`map` 和 `set` 很像,都是 STL 中會**自動排序**、**快速查找**的容器,但在儲存方式上有明顯不同。
`set` 是單純地儲存「元素本身」,而 `map` 則是用**鍵值對**(key-value pair)來儲存資料:**每個鍵(key)對應一個值(value)**。
這樣的概念,其實和我們熟悉的「**陣列**」很像——你可以把 key 想成「index」,value 就是「對應的值」。
但 `map` 比陣列強大多了,它的優勢包括:
* **key 不一定要是數字**
* **不必照順序編號**
* **自動排序 key**
* **查找、插入都很快**
可以說是「**Pro Max 版的陣列**」。
另外,由於 `map` 的每一筆資料都是「鍵值對」,在底層實作上,其實就是使用 `pair`(忘記可以複習 [【3-4】動態陣列 — vector](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/SknbSSzSxl)),也就是 `pair<key, value>` 的形式,這點我們在遍歷時會更清楚看到。
## map
`map` 是 STL 中一個用來儲存鍵值對(key-value pair)的容器,具有以下特性:
* 鍵(`key`)不重複,每個鍵對應一個值(`value`)
* 鍵自動排序(默認按鍵由小到大排序)
* 搜尋效率高(底層邏輯基於紅黑樹)
### 複雜度分析
| 操作 | 時間複雜度 | 空間複雜度 |
| -- | ------------- | -------- |
| 插入 | $O(\log n)$ | $O(n)$ |
| 刪除 | $O(\log n)$ | $O(n)$ |
| 查找 | $O(\log n)$ | $O(n)$ |
### 宣告
```cpp
map<int, string> m; // 宣告一個名稱為 m,存放「整數」鍵對應「字串」值的 map
map<int, string> m2 = {{1, "apple"}, {2, "banana"}, {3, "cherry"}}; // 初始化
```
### 插入資料
```cpp
m.insert({key, value}); // 插入一個鍵值對 (key, value) 到 map 中
m[key] = value; // 也可以用這種方式來插入鍵值對
m.at(key); // 用這種方法,若 key 不存在會報錯
```
* 若 `key` 已存在,`insert` 不會更新值,`[]` 則會覆蓋原本的值。
### 刪除資料
```cpp
m.erase(key); // 刪除指定 key 的元素
m.erase(iterator); // 刪除 iterator 所指位置的元素
m.clear(); // 清空 map
```
### 其他操作
```cpp
m.count(key); // 若 key 存在回傳 1,否則回傳 0
m.size(); // 回傳整數,表示 map 的大小
m.empty(); // 回傳布林值,若 map 為空則返回 true,否則返回 false
iterator->first // 取得 value 對應的 key
iterator->second // 取得 key 對應的 value
```
### 遍歷整個 map
忘記的話去複習[【3-5】指標](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/rJPSm8fSex)、[【3-6】迭代器](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/S16spNGrxg)
```cpp=1
for (auto p : m) {
p.first; // key
p.second; // value
}
for (auto it = m.begin(); it != m.end(); ++it) {
it->first; // key
it->second; // value
}
```
### Python對照
Python 的 `map` 沒有排序,相當於 C++ 中的 `unordered_map`,會在[【5-5】非排序關聯式容器 — unordered_set & unordered_map](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/rkIlEGcSel) 介紹。
```python
# 宣告
m = dict()
m2 = {1: "apple", 2: "banana", 3: "cherry"}
# 插入資料
m[key] = value # 插入或更新 key 對應的值
m.setdefault(key, value) # 若 key 不存在才插入,否則不變
# 存取資料
val = m[key] # 直接存取(若 key 不存在會報錯)
val = m.get(key) # 安全存取(key 不存在回傳 None)
# 刪除資料
m.pop(key, None) # 刪除指定 key,若不存在不報錯
del m[key] # 刪除指定 key,若不存在會報錯
m.clear() # 清空整個 dict
# 其他操作
key in m # 判斷 key 是否存在(類似 count)
len(m) # 回傳大小
not m # 判斷是否為空
# 遍歷
for k, v in m.items():
print(k, v) # k 相當於 key, v 相當於 value
# 排序
sorted_items = sorted(m.items()) # 依 key 排序(回傳 list of pair)
sorted_by_value = sorted(m.items(), key=lambda x: x[1]) # 依 value 排序
```
---
## multimap
`multimap` 和 `map` 類似,不同點在於
* 鍵可以重複
* 自動排序(根據 key)
### 複雜度分析
| 操作 | 時間複雜度 | 空間複雜度 |
| -- | ------------- | -------- |
| 插入 | $O(\log n)$ | $O(n)$ |
| 刪除 | $O(\log n)$ | $O(n)$ |
| 查找 | $O(\log n)$ | $O(n)$ |
### 宣告
```cpp=1
multimap<int, string> mm; // 宣告一個名稱為 mm,存放「整數」鍵對應「字串」值的 multimap
multimap<int, string> mm2 = {{1, "apple"}, {1, "banana"}, {2, "cherry"}}; // 初始化
```
### 插入資料
```cpp
mm.insert({key, value}); // 插入一個鍵值對 (key, value) 到 multimap 中
```
### 刪除資料
```cpp
mm.erase(key); // 刪除所有該 key 的元素
mm.erase(iterator); // 刪除指定位置
mm.clear(); // 清空 multimap
```
### 其他操作
```cpp
mm.count(key); // 回傳該 key 出現的次數
mm.size(); // 回傳整體鍵值對數量(含重複)
mm.empty(); // 若 multimap 為空則回傳 true
iterator->first // 取得 value 對應的 key
iterator->second // 取得 key 對應的 value
```
### 遍歷
忘記的話去複習[【3-5】指標](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/rJPSm8fSex)、[【3-6】迭代器](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/S16spNGrxg)
```cpp=1
for (auto p : mm) {
p.first; // key
p.second; // value
}
for (auto it = mm.begin(); it != mm.end(); ++it) {
it->first; // key
it->second; // value
}
```
### Python對照
Python 本身沒有 `multimap`,但可以用 `defaultdict(list)` 模擬:
```python
from collections import defaultdict
# 宣告
mm = defaultdict(list)
mm2 = defaultdict(list, {1: ["apple", "banana"], 2: ["cherry"]})
# 插入資料
mm[key].append(value) # 插入一筆鍵值對(允許重複 key)
# 刪除資料
del mm[key] # 刪除該 key 對應的所有值
mm.clear() # 清空 multimap
# 其他操作
len(mm[key]) # 回傳該 key 對應的數量(等同 mm.count(key))
sum(len(v) for v in mm.values()) # 所有鍵值對總數(含重複)
not mm # 判斷是否為空
# 遍歷
for key, values in mm.items():
for value in values:
key # 等同 iterator->first
value # 等同 iterator->second
```
---
聯絡方式:[codecodefunny@gmail.com](mailto:codecodefunny@gmail.com)
最後編修時間:2025/07/12 子柚筆