# C++ STL
---
## 什麼是STL?
* 標準模板庫(Standard Template Library),是C++內建的函式庫
* 為了可以套用不同資料型態,其實作方式都是採用**模板(template)**
* STL分成以下六個部分
* 容器 Container
* 演算法 Algorithm
* 迭代器 Iterator
* 適配器 Adaptor
* 仿函數 Function Object
* 空間配置器 Allocator
---
## C++ 標準庫的說明文件
- 官方標準文件(比較文鄒鄒):https://eel.is/c++draft/
- 第三方整理(比較容易懂):https://en.cppreference.com/w/
- 其他參考1:https://4yu.dev/post/STL/
- 其他參考2:https://io.zouht.com/154.html
## 迭代器 Iterator
* 可以用來取代指標的一種型別
* iterator有很多種,下表中愈下方的功能愈強大(i.e. 隨機存取迭代器功能最強大)

用途:
* 可以比較兩個位址是否相同
* 可以使用dereference * 來取得裡面的值
* 依照類別不同可以進行整數加減法來進行前後移,能位移多少個資料就看他是表中的哪一種迭代器
* 隨機存取迭代器:可以往前往後移x項,也可以遞增遞減(前一項後一項)
* 雙向迭代器:只能遞增遞減(前一項後一項)
* 單向迭代器:只能遞增
* 迭代器可以拿來遍歷容器裡面的內容,分為iterator跟reverse_iterator
* iterator: container.begin(), container.end()
* reverse_iterator: container.rbegin(), container.rend();

---
---
# 資料結構
## Vector
- ```#include <vector>```
- 連續的順序的儲存結構,長度可以動態調整
### 1. 常用語法
#### 建立vector - $O(n)$
```vector<datatype> arr(length, [value])```
```cpp
vector<int> arr; // 創建陣列
vector<int> arr(100); // 創建初始長度為100的陣列
vector<int> arr(100, 1); // 創建初始長度為100的陣列,陣列元素初始值為1
vector<vector<int>> dp(5, vector<int>(6)); // 創建 5 rows, 6 columns的二維陣列,初始值為0
vector<vector<int>> dp(5, vector<int>(6, 10)); // 創建 5 rows, 6 columns的二維陣列,初始值為10
// 創建5個6x4的三維陣列
vector<vector<vector<int>>> dp(5, vector<vector<int>>(6, vector<int>(4)));
```
#### 尾部增減 - $均攤O(1)$
- ```push_back()```
- ```pop_back()```
- 因為涉及長度變化,所以時間複雜度要均攤之後才會是$O(1)$
Example
```cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.pop_back();
v.pop_back();
for (auto& ele : v)
cout << ele << ' ';
cout << endl;
return 0;
}
```
#### 取得元素 - $O(1)$
- ```v[i]```(i as indices)
#### 取得長度 - $O(1)$
- ```v.size()```
#### 清空 - $O(n)$
- ```v.clear()```
#### 是否為空 - $O(1)$
* ```v.empty()```
* return bool
#### 改變長度 - $O(N)$
* ```v.resize(length, [value])```
* 改長會有初始值
* 改短會刪值
### 2. 注意事項
#### 若知道長度的話一定要提前指定長度
* 如果一開始不宣告長度,而是用迴圈push_back的話,會一直重分配空間造成時間效率不佳
#### 注意size_t overflow
```v.size()``` 的回傳值類型是size_t,會依照不同編譯器而有不同範圍
---
## Stack
- ```#include <stack>```
- 二次封裝deque的容器,實現FILO
### 1. 常用語法
#### 建立
```stack<type> stk```
#### 插入/刪除
- ```stk.push(5)```
- ```stk.pop()```
#### 取得頂部
- ```stk.top()```
#### 取得長度
- ```stk.size()```
#### 是否為空
* ```stk.empty()```
### 2. 注意事項
* 無法使用迴圈+index進行元素存取
---
## Queue
- ```#include <queue>```
- 二次封裝deque的容器,實現FIFO
### 1. 常用語法
#### 建立
```queue<type> que```
#### 插入/刪除
- ```que.push(5)```
- ```que.pop()```
#### 取得頭部元素/尾部元素
- ```que.front()```
- ```que.back()```
#### 取得長度
- ```que.size()```
#### 是否為空
* ```que.empty()```
### 2. 注意事項
* 無法使用迴圈+index進行元素存取
---
## Priority Queue
* ```#include <queue>```
### 1. 常用語法
#### 建立
```priority_queue<type, container<type>, compare> pque```
* type: 元素類型
* container: 底層容器,默認為vector\<type\>,平常使用不需要更改
* compare: 比大小,默認為less,可以自定義
<font color="orange">**預設是max heap,如果要改成min heap需要傳入greater仿函式**</font>
```cpp
priority_queue<int> pque; // max heap
priority_queue<int, vector<int>, greater<int>> pque; // min heap
```
#### 插入/刪除
- ```pque.push(5)```
- ```pque.pop()```
#### 取得頂部
- ```pque.top()```
#### 取得長度
- ```pque.size()```
#### 是否為空
* ```pque.empty()```
### 2. 使用情形
* 需要維持元素的有序性,每次都要向queue插入大小不定的元素
* 每次從queue裡面取出「最大」或「最小」的元素(數量n,插入操作數量k) - $k*log(n)$
### 3. 注意事項
* 無法使用迴圈+index進行元素存取
* 也無法使用\[\]運算子來進行賦值
---
## Set
* ```#include <set>```
提供$O(LogN)$的插入、刪除、尋找。底層原理是紅黑樹。
| 性質 | set | multiset |unordered_set |
| -------- | -------- | -------- | -------- |
| 元素要嘛在裡面,要嘛不在裡面 | O | O | O |
| 元素只能在集合裡面出現一次 | O | X (任意次) |O |
| 元素是沒有順序的 | X(升序) | X(升序) |O |
### 1. 常用語法
```set<type, compare> st;```
* type: 元素類型
* compare: 比大小,默認為less,可以自定義
#### 插入/刪除 - $O(LogN)$
* ```st.insert(4)```
* ```st.erase(4)```
* 刪除不存在的元素不會發生任何事情
#### 查找 - $O(LogN)$
* 找到:返回該元素的迭代器
* 沒找到:返回end()迭代器
```cpp
set<int> st;
if (st.find(4) == st.end()) // 迭代器為end()亦即找不到
{
// do something
}
```
#### <font color="orange">另一種查找方式,使用count()</font>
* 若元素存在,則返回個數
* 若元素不存在,則返回0
可以用以上邏輯套用到if-else condition
```cpp
if (st.count(2))
cout << "Found" << endl;
else
cout << "Not Found" << endl;
```
#### 取得長度
* ```st.size()```
#### 清空
* ```st.clear()```
#### 是否為空
* ```st.empty()```
#### 遍歷:使用迭代器
標準迴圈
```cpp
for (auto it = st.begin(); it != st.end(); ++it)
// do something
```
For each
```cpp
for (auto& ele : st)
// do something
```
#### 找min/max
```cpp
// min
cout << *st.begin();
```
```cpp
// max
cout << *st.rbegin();
```
### 2. 使用情形
* 去除重複
* 維護順序
* 判斷元素是否出現過(測資範圍如果太大,用binary array會無法做,這時就可以用set)
### 3. 注意事項
* 也無法使用\[\]運算子來進行存取
* 元素只能讀,不能修改,若要修改要先erase()
* <font color="orange">set的迭代器無法計算index</font>
---
## Map
* ```#include <map>```
| 性質 | map | multimap |unordered_map |
| -------- | -------- | -------- | -------- |
| key只能出現一次 | O | X (任意次) | O |
| key沒有順序 | X (升序) | X (升序) |O |
### 1. 常用語法
```map<key_type, value_type, compare> mp```
- key_type: key值類型
- value_type: value類型
- compare: 比較器
#### 插入 - $O(LogN)$
* ```mp[key] = value```
* 如果key值不存在,就會新增一個kv pair,value預設值會是0
* ```mp.erase(key)```
#### 查找 - $O(LogN)$
* 找到:返回該元素的迭代器
* 沒找到:返回end()迭代器
``` if (mp.find(key) == mp.end())```
#### 取得長度
* ```mp.size()```
#### 清空
* ```mp.clear()```
#### 是否為空
* ```mp.empty()```
#### 遍歷:使用迭代器
* 內部元素是pair,要取得key或value就要使用first, second
* 記得迭代器是類似指標的東西,所以要用->去訪問
```cpp
for (auto it = mp.begin(); it != mp.end(); ++it)
cout << it->first << ", " << it->second << endl;
```
For each
```cpp
for (auto& ele : st)
// do something
```
### 2. 使用情形
* 統計元素出現次數
### 3. 注意事項
* 使用\[ \]運算子時,如果key值不存在,value會變成預設值
* <font color="orange">map的迭代器無法計算index</font>
---
## String
```#include <string>```
### 1. 常用語法
#### 創建
```cpp
string s1;
string s2 = "something";
```
#### 修改、查詢
```s[i]```
#### 是否相等
```s1 == s2```
#### Concatenate
```s1 + s2```
#### Append
```s1 += s2```
#### Substring
```s1.substr(starting index, substring length)```
#### Find
```s1.find(s2, [starting index])```
if-else
<font color="orange">**string::npos**</font>會返回一個數字,如果沒找到可以直接跟這個數比
```cpp
string s = "123123456789";
if (s.find("321") == string::npos)
cout << "not found" << endl;
else
cout << "found" << endl;
```
#### Type Casting
| From | To | Syntax |
| -------- | -------- | -------- |
| int / long long / float / double / long double | string | to_string() |
| string | int | stoi() |
| string | long long | stoll() |
| string | float | stof() |
| string | double | stod() |
| string | long double | stold() |
### 2. 使用情形
萬用,不要再用字元陣列了
### 3. 注意事項
* <font color="orange">**拼接的時候一定要用 ```+=```**</font>
* .substr()**第二個參數是『子字串長度』**!
* **.find()的時間複雜度是$O(n^2)$**
---
## pair
```#include <utility>```
### 1. 常用語法
#### 創建
```cpp
pair<int, int> p1;
pair<int, long long> p2;
pair<char, int> p3;
```
#### 賦值
old version
```cpp
pair<int, char> pr = make_pair(1, 'a');
```
after c++11
```cpp
pair<int, char> pr = {1, 'a'};
```
#### 取值
* 取第一個值: ```.first()```
* 取第二個值: ```.second()```
---
---
# 演算法
## 常用函數
* swap()
* reverse()
* ```.reverse(v.begin(), v.end())```
* unique()
* 消除相鄰元素的重複值
* 單獨使用並不能達成去除重複的效果,所以要加上排序
* 消除後,尾部會出現一些無效資料,所以要配合erase
* **```arr.erase(unique(arr.begin(), arr.end()), arr.end())``` 可以直接背下來**
* Example
```cpp
vector<int> arr{1, 2, 1, 4, 5, 4, 4};
sort(arr.begin(), arr.end());
// before 1 1 2 4 4 4 5
// after 1 2 4 5 * // unique返回值就是*的位置,所以從*開始刪除到結尾
arr.erase(unique(arr.begin(), arr.end()), arr.end());
for (auto& ele : arr)
cout << ele << ", ";
```
* sort()
* 預設(升序): ```sort(v.begin(), v.end())```
* 降序: ```sort(v.begin(), v.end(), greater<int>());```
<font color="orange">**(記得greater\<int\>要加小括號)**</font>
* 特殊排序(自定義排序): <font color="orange">**記得不要加等號**</font>
```cpp
bool cmp(pair<int, int> a, pair<int, int> b)
{
if (a.second != b.second)
return a.second < b.second;
return a.first > b.first
}
int main()
{
vector<pair<int, int>> arr{{0, 1}, {2, 9}, {8, 1}, {0, 0}};
sort(arr.begin(), arr.end(), cmp);
return 0;
}
```
* **lower_bound() / upper_bound(): binary search分支,一定要排序**
* <font color="orange">**lower_bound(): 尋找 >=x 的第一個元素位置**</font>
* <font color="orange">**upper_bound(): 尋找 >x 的第一個元素位置**</font>
* <font color="orange">**因為返回的是迭代器,因此可以減去.begin()得到index**</font>
Example:
```cpp
vector<int> v{0, 1, 1, 1, 8, 9, 9};
int pos = lower_bound(v.begin(), v.end(), 8) - v.begin();
cout << pos << endl; // 4
pos = upper_bound(v.begin(), v.end(), 8) - v.begin();
cout << pos << endl; // 5
```
* max() / min()