---
# System prepended metadata

title: STL

---

# STL

## iterator
又稱迭代器，幾乎每個STL容器都有iterator。內部是一個指標，指向容器內的元素。XXX.begin()代表該容器的第一個元素的迭代器，XXX.end()代表該容器的最後一個元素的後一個代迭器。大部分的iterator支援 ++ , -- , == , != 等運算子，少部分(如vector)可以支援 + , - , > , < 等

## pair
儲存兩個元素，第一個為first，第二個是second
適合拿來存座標等等兩個一組的東西
```cpp
    pair<int,int> a; //創建一個pair，第一項與第二項都存int
    pair<int,int> b{45,67}; //創建b，並指定1,2項(沒指定的話是0)
    a.first=1; //a的第一項設為1
```


## vector
簡單來說就是陣列。內部實作也跟陣列相同，但是支援更多功能，如刪除、新增、迭代器等
```cpp
#include <bits/stdc++.h>

using namespace std;

int main()
{
    vector<int> v; //創建一個存放int的空陣列
    vector<double> v2(100); //創建一個存放double,長度為100的陣列(每項預設為0)
    vector<int> v3(30,-1); //創建一個存放int,長度為30的陣列(每項預設為-1)
    vector<int> v4={1,2,3,4,5}; //創建v4並指定內容
    v4.push_back(1); //在v4陣列尾端放入1
    cout<<v4[0]; //輸出v4陣列索引為0的元素(第1個)
    v4.pop_back(); //刪除v4陣列尾端元素
    v4.insert(v4.begin()+10,5); //在v4的第10項插入5
    v4.erase(v4.begin()+9); //刪除v4的第9項
    v4.erase(v4.begin(),v4.begin()+5); //刪除v4的第0到5項
    v.clear(); //清空v
    return 0;
}
```
因為vector與陣列一樣，資料是放在連續的記憶體中。
如果要新增第二項，必須創建一個更大的記憶體空間，並把所有元素移過去，所以時間複雜度為$O(n)$。插入也是一樣道理。

但是vector有一個特殊的機制，名為capacity(容量)
他與size不同，是預先留好的空間。這麼一來，push_back或是pop_back等最尾端的操作，就可以直接使用預留的空間而不需要搬動其他資料。複雜度$O(1)$

![](https://hackmd.io/_uploads/S1ikJ1ITn.png)
[圖源](https://shengyu7697.github.io/std-vector/)

常用操作:
```
    v.reserve(100); //將容量設為100
    v.resize(10); //將size設為10
    v.resize(10,1); //將size設為10,每一項設為1
    v.size(); //取出size
    v.capacity(); //取出capacity
```

## list
STL的linked list(鏈結串列)。鏈結串列由node(節點)組成，每個節點當中存放value(值)以及指向下一個節點的指標

![](https://hackmd.io/_uploads/B1yIpxI62.png)
[圖源](https://www.softwaretestinghelp.com/linked-list/)

因為資料並不是存在連續的記憶體中，所以不支援隨機存取。
例如要存取list的第9項時，必須從1,2,3...的順序一路走到9
，所以複雜度為$O(n)$，十分難用。

但是因為每一個節點只關聯到前一個及後一個節點，所以刪除時只需要把前一個的指標指向後一個就好，複雜度$O(1)$，插入同理。

基本用法與vector相同，只是不能用list[i]來取出第i項。如果要取第i項可以把 list.begin() ++ i次

## stack
又稱堆疊，LIFO(後進先出)的資料結構。就像一疊盤子，每次要新增只能放在最上面，而要拿走也只能拿最上面的。通常可以用vector或是linked list來實作stack

![](https://hackmd.io/_uploads/SJqM4b8a2.png)
[圖源](https://www.geeksforgeeks.org/stack-meaning-in-dsa/)

常用操作:

```cpp
    stack<int> s; //創建一個堆疊
    s.push(1); //新增元素1(在最上方)
    cout<<s.top(); //輸出最上方的元素
    s.pop(); //刪除最上方元素
```

顯而易見的，每項操作都是$O(1)$。雖然感覺很像劣化版vector，沒有什麼用處，但是需要LIFO時，因為操作比vector少，寫起來比較簡單，所以還是有一點用。

註:因為只能存最上面的那一項，所以沒有iterator

## queue
又稱貯列，FIFO(先進先出)的資料結構，就像排隊一樣，要加入的必須從最後面開始，離開的必須在最前面。跟stack同樣，可以用vector或list實作，也沒有iterator。所有操作皆為$O(1)$

```cpp
    queue<int> q; //創建一個佇列
    q.push(1); //新增元素1(最後方)
    cout<<q.front(); //輸出最前方的元素
    q.pop(); //刪除最前方元素
```

## unordered_map
STL中的hash table(雜湊表)。雜湊表由key跟value組成。key紀錄了value存放的位置。key可以是 int , double , string 等等。而把string轉成索引值(value存放的位置)的東西稱為hash_function(雜湊函數)，像是解碼器一樣。

![](https://hackmd.io/_uploads/rktIiZ8a3.png)
[圖源](https://joe-chang.medium.com/%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B-hash-table-%E9%9B%9C%E6%B9%8A%E8%A1%A8-ffec829a4116)

那你可能會想，不會有兩個不同的key算出同一個索引值的情況嗎?
畢竟函數是可以多對一的。這種情況稱為碰撞，處理碰撞有數種方法，都已經寫在unordered_map裡面打包送你，雜湊函數也包含在裡面，我們只需要知道概念就好了。

常見操作:

```cpp
    unordered_map<string,int> h(100); //創建一個雜湊表，key類型string，value類型int，大小100
    h["kspgc"]=100; //把kspgc的value設為100
    cout<<h["kspgc"]; //輸出kspgc的value
    h.erase("kspgc"); //移除kspgc
    cout<<h["kspgc"]; //找不到kspgc，會輸出0
```

插入、查詢、刪除只需要經過一次hash_func計算，複雜度$O(1)$

註:unordered_map的迭代器指向一個pair{key,value}


## map/set
BST(二元搜尋樹)，想法類似二分搜。由節點組成，每個裡面存key,value以及左右子節點。首先有一個根節點，插入的時候，如果新節點的key比較小，就往根節點的左節點移動，如果該節點沒有左節點，那新的節點就會成為他的左節點，反之則往右。

![](https://hackmd.io/_uploads/BkgKPfUa2.png)
[圖源](https://blog.twshop.asia/%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B-%E4%BA%8C%E5%85%83%E6%A8%B9-binary-tree/)

查詢的時候，跟插入同樣，比該節點小就往左。因為每次可以走就可以刪掉比那個節點大/小的所有數字，複雜度是$O(\log n)$。刪除的時候需要分成三種狀況，但是複雜度仍然是$O(log n)$。

但是如果二元樹的太傾斜，如斜曲二元樹:

![](https://hackmd.io/_uploads/SkvVWmIpn.jpg)
[圖源](http://yhhuang1966.blogspot.com/2018/06/c.html)

那麼每次查詢都要走完整條，時間複雜度會變成$O(n)$。於是就誕生了二元平衡樹，也就是會動態調整的二元樹，確保左邊跟右邊的數量是差不多的。而紅黑樹是二元平衡樹的一種，原理十分複雜，我也搞不懂，但是他很快，而且確保複雜度都是$O(log     n)$。

而map跟set就是STL提供的紅黑樹，其中set的value跟key完全相同，所以只需要設定一個就好了。

常用操作:

```cpp
    map<int,string> m; //創建m，key為int,value為string
    set<int> s; //創建s，存int
    m.insert({10,"jasper"}); //插入key=10,value="jasper"的元素
    s.insert(100); //插入100
    m.find(10); //找到key為10的節點，會回傳一個iterator
    s.find(100); //找到100，會回傳一個iterator
    m.erase(10); //把key=10的點刪掉
    s.erase(100); //把100的刪掉
    m.begin(); //最小節點的iterator
    s.begin(); //最小值的iterator
```

map的iterator++為找到下一個大於他的最小值，花費$O(1)$

註:map的迭代器指向一個pair{key,value}

## multi map/set
如果新插入的key原本就存在map中時，map會自動把新的去掉，確保裡面沒有重複。而multimap則是允許重複的map，大部分的操作與map相同，除了:
```cpp
    m.erase(10); //刪除所有key=10的資料
    m.erase(m.find(10)); //刪除第一個key=10的資料
    m.count(10); //key=10的節點數量
```

### 練習
[cses: Two Sum](https://cses.fi/problemset/task/1640)
[cses: Concert Tickets](https://cses.fi/problemset/task/1091)


## 排序/二分搜
STL內建的排序sort，複雜度$O(n\log n)$，(似乎)只能用在vector或陣列上

```cpp
    vector<int> v={1,3,5,2,4,6,9};
    sort(v.begin(),v.end(),greater<int>()); //從大到小牌
    sort(v.begin(),v.end(),less<int>()); //從小到大排
    sort(v.begin(),v.end()); //沒有寫就是less
```

### 練習
[zj:a104](https://zerojudge.tw/ShowProblem?problemid=a104)
[TCIRC:d010](https://judge.tcirc.tw/ShowProblem?problemid=d010)
[TCIRC:d011](https://judge.tcirc.tw/ShowProblem?problemid=d011)

### 自訂排序方式
如果你想用一些奇怪的規則排序，例如用個位數的大小排序:

```cpp
#include <bits/stdc++.h>
using namespace std;

bool cmp(int a,int b){
    return a%10<b%10;
}
int main()
{
    vector<int> v={91,43,25,52,74,86,109};
    sort(v.begin(),v.end(),cmp);
    return 0;
}
```
結果:
![](https://hackmd.io/_uploads/r1XryV8ah.png)

練習:[zj:a528](https://zerojudge.tw/ShowProblem?problemid=a528)

### 二分搜

限定已排序的資料。每次查找資料中間，太大往左，太小往右。每次保證刪掉一半的資料，複雜度$O(\log n)$

![](https://hackmd.io/_uploads/rJPrXVITn.jpg)
[圖源](https://ithelp.ithome.com.tw/articles/10280844)

STL內建兩種二分搜(記得先sort):
```cpp
    lower_bound(v.begin(), v.end(), 10); //回傳v中"大於或等於"10的最小值的數的iterator
    upper_bound(v.begin(), v.end(), 10); //回傳v中"大於"10的最小值的數的iterator
    //如果找不到會回傳v.end()
```

### 練習
[TCIRC:b063](https://judge.tcirc.tw/ShowProblem?problemid=b063)
[ZJ:b315](https://zerojudge.tw/ShowProblem?problemid=b315)
[CSES: Factory Machines](https://cses.fi/problemset/task/1620)
[CSES: Multiplication Table](https://cses.fi/problemset/task/2422)
[APCS:牆上海報](https://zerojudge.tw/ShowProblem?problemid=h084)
[APCS:基地台](https://zerojudge.tw/ShowProblem?problemid=c575)
[TOI 初選:裁員風暴](https://zerojudge.tw/ShowProblem?problemid=k185)
[高市賽:開票策略](https://zerojudge.tw/ShowProblem?problemid=f430)
