---
# System prepended metadata

title: STL Containers
tags: [' note', books]

---

---
tags: books, note
---
# STL Containers
> Standard Template Library Containers

## Intro

STL container是C++標準函式庫中我認為最常用的部分，跟`<algorithm>`幾乎同等級

而STL container顧名思義就是容器(container)
因為是用模板(template)實作，所以可以透過更改宣告時的`<>`來裝不同的內容

STL containers在的不同容器都有不同的特性，有些功能一樣，有些不一樣
接下來會分別介紹各個容器，以及介紹個容器中相同及不同的函數

---

## Vector

使用頻率: :star: :star: :star: :star: :star:
> 取代掉所有的陣列

使用需先引入標頭檔```<vector>```
```cpp
#include <vector>
```

vector就像陣列，是用下標來存取的容器，與其不同的地方在於vector可變大小
常用於解決存取未知資料量的窘境 再加上方便又好用的成員函數 
使得很多人直接用它取代陣列 不過陣列依舊有它的優勢在
例如: 程式碼短 常數小等等
就看程式員該如何決定

但值得注意的一點是 當資料量龐大時
陣列無法一次抓那麼大的記憶體區塊 這時就只能用vector了

### 特色
- 為可變大小的序列容器
- 支援隨機(用下標)存取 $O(1)$
- 尾端增刪元素很快 $O(1)$
- 中間增刪元素很費時 $O(n)$

### 功能
- [(constructor)](#(constructor))
- [iterators](#iterators)
- [size](#size)
- [resize](#resize)
- [empty](#empty)
- [operator[]](#operator[])
- [at](#at)
- [front](#front)
- [back](#back)
- [data](#data)
- [assign](#assign)
- [push_back](#push_back)
- [pop_back](#pop_back)
- [insert](#insert)
- [erase](#erase)
- [swap](#swap)
- [clear](#clear)
- [emplace](#emplace)
- [emplace_back](#emplace_back)

---

## Deque
使用頻率: :star: :star: :star:
> Double ended queue
> 一般不常用，但其他很多容器都是用他當作基底

使用前需引入標頭檔`<deque>`
```cpp
#include <deque>
```

基本上就是多了push_front, pop_front 的 vector
如果真的需要操作頭尾的情況時 才會選則deque
不然基本上都是使用vector

### 特色

- 操作特色與vector類似 時間複雜度一樣但常數較大

### 功能
- [(constructor)](#(constructor))
- [iterators](#iterators)
- [size](#size)
- [resize](#resize)
- [empty](#empty)
- [operator[]](#operator[])
- [at](#at)
- [front](#front)
- [back](#back)
- [data](#data)
- [assign](#assign)
- [push_back](#push_back)
- [push_front](#push_front)
- [pop_back](#pop_back)
- [pop_front](#pop_front)
- [insert](#insert)
- [erase](#erase)
- [swap](#swap)
- [clear](#clear)
- [emplace](#emplace)
- [emplace_back](#emplace_back)
- [emplace_front](#emplace_front)

---

## Stack

使用頻率: :star: :star: :star:
> 進行爆搜時會用到，也可以用來取代遞迴

使用需先引入標頭檔```<stack>```
```cpp
#include <stack>
```

預設以Deque為基底的容器配接器(Container Adaptors)
主要是為了方便程式員能夠較清楚明白此容器遵守LIFO(FILO)
什麼是LIFO? 就是Last In First Out後進先出
就如同堆疊盤子一般
最後所疊上去的盤子 一定是最先拿走的盤子
~~不可能從中間抽出來吧~~
因此Stack也稱作堆疊
![](https://i.imgur.com/MONRMEr.png =500x)


### 特色

- Last In First Out

### 功能
- [(constructor)](#(constructor))
- [empty](#empty)
- [size](#size)
- [top](#top)
- [push](#push)
- [emplace](#emplace)
- [pop](#pop)
- [swap](#swap)

---

## Queue

使用頻率: :star: :star:
> 進行爆搜時會用到，除此之外很少

使用需先引入標頭檔```<queue>```
```cpp
#include <queue>
```

Queue與Stack類似 皆為以Deque為基底的容器配接器(Container Adaptors)
那Queue又要遵守啥呢? 沒錯就是FIFO(LILO)
First In First Out又是甚麼概念呢?
可以把Queue想像成一列隊伍
先排隊的人當然會是最先出來的 反之亦然
~~一樣 不可能有人插隊吧~~
因此Queue也被稱作佇列
![](https://i.imgur.com/4ykkoYD.png =500x)



### 特色

- First In First Out

### 功能
- [(constructor)](#(constructor))
- [empty](#empty)
- [size](#size)
- [front](#front)
- [back](#back)
- [push](#push)
- [emplace](#emplace)
- [pop](#pop)
- [swap](#swap)

---

## Priority queue
使用頻率: :star: :star: :star: :star:
> greedy常用，性質佳

使用需先引入標頭檔```<queue>```
```cpp
#include <queue>
```

Priority_Queue是預設以vector做為基底 經過設計的容器配接器(Container Adaptors)
那Priority_Queue又要遵守甚麼原則呢?
> 容器的排序方式會讓具有最大值的項目一定在佇列中的第一個

沒錯神奇吧
你可能會好奇這是怎麼做到的 可以上網搜尋Heap的相關概念

在大概了解Heap的概念後 就會知道Heap的建立是能夠自訂比較方式的
而Prioriy_Queue模板也提供了參數來讓程式員能自訂比較型態
預設參數為最大堆(Max-Heap) 也就是Top為最大值

### 特色

- 超級無敵方便存取資料極值
- 插入、刪除 $O(logn)$
- 讀取極值 $O(1)$

### 功能
- [(constructor)](#(constructor))
- [empty](#empty)
- [size](#size)
- [top](#top)
- [push](#push)
- [emplace](#emplace)
- [pop](#pop)
- [swap](#swap)

---

## Set

使用頻率: :star: :star: :star:
> 好用，但priority_queue更常用一點

使用需先引入標頭檔```<set>```
```cpp
#include <set>
```

Set是按照特定順序存取唯一元素的容器
通常是拿來判斷資料中有無重複
我們一般會稱存放的資料為金鑰(Keys)

### 特色

- 資料不重複 
- insert/erase $O(logn)$

### 功能
- [(constructor)](#(constructor))
- [iterators](#iterators)
- [empty](#empty)
- [size](#size)
- [insert](#insert)
- [erase](#erase)
- [swap](#swap)
- [clear](#clear)
- [emplace](#emplace)
- [find](#find)
- [count](#count)
- [lower_bound](#lower_bound)
- [upper_bound](#upper_bound)
- [equal_range](#equal_range)

---

## Map

使用頻率: :star: :star: :star: :star:
> 本斥但香

跟set類似 只不過每一個key是對應一個type
key -> 索引的資料類型
type -> 對應項目的資料類型

通常也是處理資料間的關係問題
 
### 特色

- 好用 :D

### 功能
- [(constructor)](#(constructor))
- [iterators](#iterators)
- [empty](#empty)
- [size](#size)
- [operator[]](#operator[])
- [at](#at)
- [insert](#insert)
- [erase](#erase)
- [swap](#swap)
- [clear](#clear)
- [emplace](#emplace)
- [find](#find)
- [count](#count)
- [lower_bound](#lower_bound)
- [upper_bound](#upper_bound)
- [equal_range](#equal_range)

---

## Set跟Map的變種

以下這些能用的函式都跟原版幾乎一樣
但在特性上有些不同，故獨立出來解釋

### Multiset & Multimap

使用頻率: :star: :star: 

#### 特色
- 同樣的鍵值可重複

### Unordered_set & Unordered_map

使用頻率: :star: :star:

#### 特色
- 利用hash table實作，內部不排序
- 插入、搜尋都是$O(1)$，但常數大，慎用


### Unordered_multiset & Unordered_multiset

使用頻率: :star:

:::success
**舉一反三**
你能從名字推出這兩個容器的特色是什麼嗎?
:::

:::spoiler 想完再打開
#### 特色
結合以上兩種特點
- 同樣的鍵值可重複
- 內部不排序
:::



---

## 函式介紹



### (constructor)
建構元，如果不知道可以去[Class那篇](https://hackmd.io/@konchin/book/%2FXToC9yD-R6Oxo7-G8nMy6Q/#Constructor)參考

#### 用法

一般來說有幾種不同的方式
1. **初始大小**
```cpp
stl<type> container(size);
// vector<int> vec(10);
```
- `size`為建構時設定的容器大小
- 可用容器清單
    - [x] Vector
    - [x] Deque
    - [ ] Stack
    - [ ] Queue
    - [ ] Priority_queue
    - [ ] Set
    - [ ] Map

2. **大小+預設值**
```cpp
stl<type> container(size, val);
// vector<int> vec(10, 0);
```
- `val`為建構時設定的預設值
- 可用容器清單
    - [x] Vector
    - [x] Deque
    - [ ] Stack
    - [ ] Queue
    - [ ] Priority_queue
    - [ ] Set
    - [ ] Map

3. **根據範圍**
```cpp
stl<type> container(begin, end);
// vector<int> vec2(vec.beign(), vec.end());
```
- `begin`和`end`為左閉右開的iterator
- 可用容器清單
    - [x] Vector
    - [x] Deque
    - [ ] Stack
    - [ ] Queue
    - [x] Priority_queue
    - [x] Set 
    - [x] Map

4. **複製**
```cpp
stl<type> container(another_container);
// vector<int> vec3(vec);
// stack<int, vector<int>> stk(vec3);
```
- `another_container`是同型態或是其基底的另一個容器 
- 可用容器清單
    - [x] Vector
    - [x] Deque
    - [x] Stack
    - [x] Queue
    - [x] Priority_queue
    - [x] Set 
    - [x] Map

---

### iterators
中文來講就是迭代器，與指標差不多
(指標不熟的人要去[Pointer](/@konchin/book/%2FNbR3-VgcTOemy93z6H25QA)複習喔)
不過不同的是兩者不能混用
且有些iterator不能用加法運算取得後幾位的元素

而關於STL container的迭代器有許多函式
在這就一組一組介紹

#### begin & end
為容器第一個位置與**最後一個位置後一格**的迭代器
![](https://i.imgur.com/oiPKDyB.png)

##### 用法
```cpp
stl<type>::iterator l = container.begin();
stl<type>::iterator r = container.end();
// vector<int>::iterator it;
// for(it = vec.begin(); it != vec.end(); ++it)
//     cout << *it << ' ';
```

#### rbegin & rend
為容器最後一個位置與**第一個位置前一格**的迭代器
![](https://i.imgur.com/HGdfcMR.png)

##### 用法
```cpp
stl<type>::reverse_iterator l = container.rbegin();
stl<type>::reverse_iterator r = container.rend();
```

#### cbegin& cend
與begin & end相同，不過只能讀不能修改

##### 用法
```cpp
stl<type>::const_iterator l = container.cbegin();
stl<type>::const_iterator r = container.cend();
```

#### crbegin & crend
:::success
**舉一反三**
你能從名字推出這兩個函式是什麼嗎?
對應的iterator型態又是什麼呢?
:::

:::spoiler 想完再打開
同時具有rbegin & rend和cbegin & cend的性質
為容器最後一個位置與**第一個位置前一格**，不能修改值的迭代器

##### 用法

```cpp
stl<type>::const_reverse_iterator l = container.crbegin();
stl<type>::const_reverse_iterator r = container.crend();
```
:::

---

### size
取得容器的大小，型態為`size_t`(與`unsigned long`相同)

#### 用法
```cpp
size_t n = container.size();
// vector<int> vec = {1, 2, 3};
// cout << vec.size(); //Print: 3
```

---

### resize
重設容器的大小

#### 用法
1. **以預設值為 0 重設大小**
如果設定的容器大小比原本大
多出來的部分會預設為0
```cpp
container.resize(size);
```
- `size`為欲設定之大小

2. **以自訂預設值重設大小**
如果設定的容器大小比原本大
多出來的部分會預設為自訂值
```cpp
container.resize(size, val);
```
- `val`為自訂預設值

---

### empty
檢查容器是否為空

#### 用法
```cpp
bool b = container.empty();
// if(vec.empty())
//     cout << "Vector is empty" << endl;
```

---

### operator[]
根據下標存取容器第$n$項的參照

#### 用法
```cpp
data_type& x = container[position];
//int last_element = vec[vec.size() - 1];
```
- `data_type`為`container`內容物的型態
- `position`為一個`size_t`代表下標

### at
根據下標存取容器第$n$項的參照

#### 用法
```cpp
data_type& x container.at(position);
// int last_element = vec.at(vec.size() - 1);
```
- `data_type`為`container`內容物的型態
- `position`為一個`size_t`代表下標

:::danger
**重點**

當`position`超出`container`範圍的時候
`operator[]`和`at`會有不同的反應

**operator[]**
Undefine Behavior
```cpp
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> container(5);
	cout << container[5] << endl;
	return 0;
}
```
基本上會是系統殘值
e.g. 7953

**at**
執行錯誤
```cpp
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> container(5);
	cout << container.at(5) << endl;
	return 0;
}
```
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 5) >= this->size() (which is 5)

---

因此如果懶得自己檢查有沒有超出邊界範圍
可以用`at`幫你找 :poop:
:::

---

### front
存取第一個元素

#### 用法
```cpp
data_type& x = container.front();
// int first_element = vec.front();
```
- `data_type`為`container`內容物的型態

---

### back
存取最後一個元素

#### 用法
```cpp
data_type& x = container.back();
// int first_element = vec.back();
```
- `data_type`為`container`內容物的型態

---

### top
存取最上面的元素

#### 用法
```cpp
data_type& x = container.top();
// deque<int> dq = {1, 4, 5, 3};
// stack<int> stk(dq);
// cout << stk.top(); // Print: 3
```
- `data_type`為`container`內容物的型態

---

### data
存取第一項元素的指標

#### 用法
```cpp
data_type* p = container.data();
// vector<int> vec = {1, 4, 5, 3};
// int* ptr = vec.data();
// for(size_t i = 0; i < vec.size(); ++i)
//     cout << *(ptr + i) << ' ';
// Print: 1 4 5 3
```
- `data_type`為`container`內容物的型態

---

### assign
分配容器新的內容替換當前內容 並修改大小

#### 用法
一般來說有幾種不同的方式

1. **大小+預設值**
```cpp
container.assign(size, val);
// vector<int> vec;
// vec.assign(5, 87); // vec -> 87, 87, 87, 87, 87
```
- `size`為設定的容器大小
- `val`為設定的預設值

2. **根據範圍**
```cpp
container.assign(begin, end);
// vector<int> vec1 = {1, 5, 4, 2};
// vector<int> vec2 = {0, 0}; // vec2 -> 0, 0
// vec2.assign(vec1.begin(), vec1.end()); // vec2 -> 1, 5, 4, 2
```
- `begin`和`end`為左閉右開的`iterator`

3. **initializer_list**
```cpp
container.assign(list);
// vector<int> vec;
// vec.assign({87, 48763, 0}) // vec -> 87, 48763, 0
```
- `list`為一個`initializer_list`

---

### push

從容器中增加元素
[Stack](#Stack)會加在[top](#top)
[Queue](#Queue)會加在[back](#back)

#### 用法
```cpp
container.push();
```

---

### pop

從容器中移除元素
[Stack](#Stack)會移除[top](#top)
[Queue](#Queue)會移除[front](#front)

#### 用法
```cpp
container.pop();
```

---

### push_back
在容器後面增加元素

#### 用法
```cpp
container.push_back(val);
// vector<int> vec;
// vec.push_back(87);
```
- `val`為與`container`內容物型態相同的一元素

---

### push_front
在容器前面增加元素

#### 用法
```cpp
container.push_front(val);
```
- `val`為與`container`內容物型態相同的一元素

---

### pop_back
從容器後面減少元素

#### 用法
```cpp
container.pop_back();
```

---

### pop_front
從容器前面減少元素

#### 用法
```cpp
container.pop_front();
```

---

### insert
在位置上插入元素

#### 用法
1. **Set**
```cpp
container.insert(val);
```
- `val`為與`container`內容物型態相同的一元素

2. **Map**
```cpp
container.insert(pval);
container.insert({key, val});
```
- `pval`為一個`pair`
- `key`為和鍵值同型態的一元素
- `val`為和資料同型態的一元素

:::success
**想想看**
為什麼`{key, val}`可以當作`pair`?
> :::spoiler 想完再打開
> `pair`的`initializer_list`建構
> :::
:::


3. **其他容器**
```cpp
container.insert(position, val);
```
- `position`為表示插入位置的`iterator`
- `val`為欲插入之元素

---

### erase
刪除特定位置的元素

#### 用法
1. **Set**
```cpp
container.erase(val);
```
- `val`為欲刪除的值

2. **Map**
```cpp
container.erase(val);
```
- `val`為欲刪除的鍵值

3. **其他容器**
```cpp
container.erase(position);
```
- `position`為欲刪除元素的位置

---

### swap
將兩個容器交換

#### 用法
```cpp
container.swap(another_container);
```
- `another_container`為另一個同型態的容器

---

### clear
清空容器

#### 用法
```cpp
container.clear();
```

---

### emplace
與 [insert](#insert) 相同，不過在傳入時呼叫建構元

#### 用法
1. **Set**
```cpp
container.emplace(args...);
```
- `args`為`container`內容物型態建構元所需的元素

2. **Map**
```cpp
container.emplace(key, val);
```
- `key`為和鍵值同型態的一元素
- `val`為和資料同型態的一元素

3. **其他容器**
```cpp
container.emplace(position, args...);
```
- `position`為表示插入位置的`iterator`
- `args`為`container`內容物型態建構元所需的元素


---

### emplace_back
與 [push_back](#push_back) 相同，不過在傳入時呼叫建構元

#### 用法
```cpp
container.emplace_back(args...);
// vector<pair<int, int>> vec;
// vec.emplace_back(87, 54);
```
- `args`為`container`內容物型態建構元所需的元素

---

### emplace_front
與 [push_front](#push_front) 相同，不過在傳入時呼叫建構元

#### 用法
```cpp
container.emplace_front(args...);
```
- `args`為`container`內容物型態建構元所需的元素

---

### find
找尋容器內資料(或鍵值)，有則回傳該元素之iterator，若無則回傳 [container.end()](#iterators)

#### 用法
```cpp
stl<type>::iterator it = container.find(val);
```
- `val`為欲尋找之值

---

### count
回傳數值在範圍內出現次數

#### 用法
```cpp
size_type cnt = container.count(val);

// 因為set內元素唯一 若且為若cnt = 1, 則set含此元素
```
- `val`為欲查詢之值
- `size_type`為一無號整數型態

---

### lower_bound

回傳第一個大於等於詢問值元素之iterator

#### 用法
```cpp
stl<type>::iterator it = container.lower_bound(val);
//auto it = myset.lower_bound(87);
//if(it != myset.end())
//    cout << *it << '\n';
```
- `val`為欲查詢之值

---

### upper_bound
跟lower_bound類似 不過是換成大於而已

回傳第一個大於詢問值元素之iterator

#### 用法
```cpp
stl<type>::iterator it = container.upper_bound(val);
//auto it = myset.upper_bound(87);
//if(it != myset.end())
//    cout << *it << '\n';
```
- `val`為欲查詢之值

---

### equal_range
也跟前兩個類似 只不過回傳的是等值範圍 $[begin, end)$

#### 用法
```cpp
pair<stl<type>::iterator, stl<type>::iterator> range = container.equal_range(val);

//auto p = myset.equal_range(87);
//for(auto it = p.first; it != p.second; ++it)
//    cout << *it << ' ';
```
- `val`為欲查詢之值

---

## 外部連結

如果你覺得講義不夠詳細或是覺得夠電想看英文的詳細說明

[cplusplus.com](http://www.cplusplus.com/reference/stl/)

這是可以參考的地方
基本上講義大部分內容內容都跟上面一樣，但網站講得更深入一點

> [color=#1f1e33][name=9th進階教學][time=Sun, Jun 21, 2020 12:29 PM]

> [color=#FF0000][name=9th教學顧問][time=Fri, Jun 26, 2020 15:03]