# 【5-3】排序關聯式容器 — set & multiset
在 [【5-2】排序演算法 & sort](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/Byv_lBUHel) 我們學到了排序資料的方法,但每次都宣告一個陣列,再用 `sort()`,一來效率極差,二來這是一次性的排序,排序後如果再插入新的資料,並不會自動排序,導致必須再用一次 `sort()`。
舉例原先資料排序如下:
```
5 3 1 2 4
```
經過 `sort()` 排序後:
```
1 2 3 4 5
```
再插入三筆資料:
```
1 2 3 4 5 8 6 7
```
這時候就要再用一次 `sort()`:
```
1 2 3 4 5 6 7 8
```
但別忘了,`sort()` 的時間複雜度是 $O(n\log n)$,一直用可能會吃 TLE!
---
## set
`set` 是 STL 中一個用來儲存元素的容器,具有以下特性:
* 元素**不重複**
* 自動**排序**(從小到大)
* **搜尋效率高**(底層為紅黑樹)
### 複雜度分析
| 操作 | 時間複雜度 | 空間複雜度 |
| --------- | ------------- | -------- |
| 插入 | $O(\log n)$ | $O(n)$ |
| 刪除 | $O(\log n)$ | $O(n)$ |
| 查找 | $O(\log n)$ | $O(n)$ |
### 宣告
```cpp
set<int> s; // 宣告一個名稱為 a,存放整數的 set
set<int> s2 = {10, 20, 30, 40, 50}; // 初始化列表
```
### 插入資料
```cpp
s.insert(num); // 插入 num 到 set 中,自動排序,不會重複
```
### 刪除資料
```cpp
s.erase(sum); // 將 sum 從 set 中刪除
s.erase(iterator); // 刪除 iterator 所指位置的元素
s.clear(); // 清空 set
```
### 其他操作
```cpp
s.size(); // 回傳 set 的大小(整數)
s.empty(); // 若為空回傳 true,否則 false
```
### Python對照
Python 的 `set` 沒有排序,相當於 C++ 中的 `unordered_set`,會在[【5-5】非排序關聯式容器 — unordered_set & unordered_map](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/rkIlEGcSel) 介紹。
```python
# 宣告
s = set()
s2 = {10, 20, 30, 40, 50}
# 插入資料
s.add(num) # 插入 num(實際上是無序集合,但可轉成 list 再排序),不會重複
# 刪除資料
s.discard(sum) # 若 sum 存在就刪掉,不報錯
s.clear() # 清空 set
# 其他操作
len(s) # 回傳 set 的大小
not s # 判斷是否為空,空集合為 True
# 排序
sorted_list = sorted(s) # 轉成 list 再排序
```
## multiset
`multiset` 和 `set` 類似,不同點在於:
* **允許重複元素**
* 自動排序(從小到大)
### 複雜度分析
| 操作 | 時間複雜度 | 空間複雜度 |
| --------- | ------------- | -------- |
| 插入 | $O(\log n)$ | $O(n)$ |
| 刪除 | $O(\log n)$ | $O(n)$ |
| 查找 | $O(\log n)$ | $O(n)$ |
### 宣告
```cpp
multiset<int> ms; // 宣告一個 multiset 存放整數
multiset<int> ms2 = {10, 20, 20, 30}; // 初始化列表,可儲存重複值
```
### 插入資料
```cpp
ms.insert(num); // 同 set,但插入值重複不會覆蓋
```
### 刪除資料
```cpp
ms.erase(ms.find(num)); // 僅刪除一個 num(find 是 iterator,待會會介紹)
ms.erase(num); // 刪除所有 num
```
### 其他操作
```cpp
ms.count(num); // 回傳值為 num 的數量
ms.size(); // 回傳整體元素數量(含重複)
ms.empty(); // 若為空回傳 true,否則 false
ms.clear(); // 清空 multiset
```
### Python對照
Python 本身沒有 `multiset`,但可以用 `collections.Counter` 模擬:
```python
from collections import Counter
# 宣告
ms = Counter()
ms2 = Counter([10, 20, 20, 30])
# 插入資料
ms[num] += 1 # 插入一個 num(允許重複)
# 刪除資料
if ms[num] > 0:
ms[num] -= 1 # 僅刪除一個 num
if ms[num] == 0:
del ms[num]
del ms[num] # 刪除所有 num(等同 erase(num))
# 其他操作
ms[num] # 回傳 num 的數量
sum(ms.values()) # 回傳整體元素數量(含重複)
not ms # 判斷是否為空
ms.clear() # 清空 multiset
```
---
## 進階操作 — find()
我們可以使用 `find()` 來用 iterator 指向想查找的元素。忘記 iterator 可以去複習 [【3-5】指標](https://hackmd.io/@LAfWxjSxRASps-4O_bppsA/rJPSm8fSex)。
```cpp=1
#include <bits/stdc++.h>
using namespace std;
int main() {
set<int> numbers = {10, 20, 30, 40, 50};
int target = 30;
auto it = numbers.find(target);
// it 為指標,指向 target 的位置
// 若沒找到,it == numbers.end()
if (it != numbers.end()) {
cout << "Found " << target << " in the set." << endl;
} else {
cout << target << " not found in the set." << endl;
}
return 0;
}
```
### Python對照
```python
numbers = {10, 20, 30, 40, 50}
target = 30
if target in numbers:
print(f"Found {target} in the set.")
else:
print(f"{target} not found in the set.")
```
---
聯絡方式:codecodefunny@gmail.com
最後編修時間:2025/07/12 子柚筆