# STL 在C++裡面的函式中,有一些內建好用資料結構,這些資料結構都是用一種叫型別模板(template)的東西去包裝,而這些資料結構包裝起來就變成了標準模板庫(standard template library) 那STL裡面有什麼呢? 首先我們先來講解STL的三大容器 vector、deque跟list ## vector ### vector介紹 原生C++的陣列是使用一連串的記憶體位置來儲存資料,但是當我們宣告完陣列大小後就沒辦法再修改了。 這時候我們就可以用STL裡的vector!! vector簡單來講就是一個可以變長變短的陣列。在某些情況下,可以伸縮的陣列在解題上可以方便許多。 首先,我們來宣告一個vector,以下是一些常見的vector宣告方法 ```cpp= vector<int> vec; //一般的宣告 vector<int> vec = {1, 2, 3}; //宣告一個有1,2,3的vector vector<int> vec(10); //宣告長度為10的vector vector<int> vec(10,1); //宣告長度為10的vector,並將全部填為1,如果後面沒有參數,則填入0 ``` 如果我們已經知道資料的大小是多少的話,就可以把vector當一般陣列一樣。 那如果我們不知道要輸入多大的資料,我們可以使用push_back(emplace_back)來自動新增元素並配置記憶體到vector的尾端 ```cpp= vector<int> vec; vec.push_back(1); vec.emplace_back(1); ``` push_back跟emplace_back是差不多的東西,但在某些地方上有些微的不同 以pair當作例子來看 ```cpp= vector<pair<int,int>> v; //before C++11 v.push_back(make_pair(1, 1)); v.push_back({1, 1}); //C++11 v.emplace_back(1, 1); ``` push_back是先建構一個臨時的物件,複製到容器尾端裡後再釋放臨時物件的記憶體 而emplace_back是直接呼叫建構函式建構在容器尾端 所以通常emplace_back的效率會比push_back還來的好 以下是vector常用的操作 `vec.size()` 回傳vector的大小 $O(1)$ `vec.reserve(size)`預留size大小的空間 如果要重新配置記憶體 $O(n)$,否則 $O(1)$ `vec.empty()` 回傳一個布林值,如果vector是空的就回傳true,否則回傳false, $O(1)$ `vec.clear()` 清空vector,$O(n)$ ### vector常見用途 以下兩種是vector的遍歷方式 ```cpp= vector<int> vec = {1, 2, 3}; for(int i=0;i<v.size();i++) cout << v[i] << ' '; for(int i: vec) cout << i << ' '; // C++11 以上 ``` 可以看個人喜好或是情況來決定要用哪一種遍歷方式 vector的迭代器是屬於隨機存取迭代器,所以說我們可以把兩個vector的指標做運算,再搭配二分搜求出任一個數字在第幾個位置 ```cpp= vector<int> vec; vec.begin() //vector的第一個位置 vec.end() //vector最後一個位置的後一位 int N = vec.end() - vec.begin(); //算出vec的size ``` 迭代器也可以拿來遍歷vector ```cpp= for(auto it = vec.begin(); it != vec.end(); it++) cout << *it; ``` vector也可以拿來當作連接串列,用來存圖上的邊和權重 ```cpp= vector<int> vec1[N]; for (int i = 0; i < m; ++i) { //無權單向 int a, b; cin >> a >> b; vec[a].push_back(b); } vector<pair<int,int>> vec2[N]; for (int i = 0; i < m; ++i) { //有權雙向 int a, b, w; cin >> a >> b >> w; vec2[a].push_back(make_pair(b, w)); vec2[b].push_back(make_pair(a, w)); } ``` ### 注意事項 vector在配置記憶體時蠻花時間的,如果確定好大小的話,先reserve()後再用push_back,效率會比較好 ### 習題 [TOJ 575](https://toj.tfcis.org/oj/pro/575/) [zerojudge b298](https://toj.tfcis.org/oj/pro/575/) :::spoiler 解答 TOJ 575 ```cpp= #include <bits/stdc++.h> using namespace std; #define bobotou ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0) #define F first #define S second using pii = pair<int, int>; const int MAXN = 1000005; vector<int> vec[MAXN]; vector<pii> peo; signed main(){ bobotou; int N, K; cin >> N >> K; for (int i = 0; i < K; i++){ int tmp1, tmp2; cin >> tmp1 >> tmp2; peo.emplace_back(tmp1, tmp2); } for(int i = 0; i < K; i++){ vec[peo[i].F].emplace_back(peo[i].S); vec[peo[i].S].emplace_back(peo[i].F); } for(int i = 1; i <= N; i++){ sort(vec[i].begin(),vec[i].end()); for(int j = 0; j < vec[i].size(); j++){ if(j != 0) cout << ' '; cout << vec[i][j]; } cout << '\n'; } } ``` zerojudge b298 (vector + queue) ```cpp= #include <bits/stdc++.h> using namespace std; #define bobotou ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0) #define pb(x) push_back(x) #define F first #define S second const int MAXN = 100005; vector<int> vec[MAXN]; bool poll[MAXN]; signed main(){ bobotou; int N, M , L , Q; int tmp1, tmp2; cin >> N >> M >> L >> Q; for(int i = 0; i < M; i++){ cin >> tmp1 >> tmp2; vec[tmp1].pb(tmp2); } queue<int> q; for(int i = 0; i < L; i++){ cin >> tmp1; q.push(tmp1); while(!q.empty()){ int x = q.front(); q.pop(); if(poll[x]) continue; poll[x] = true; for(auto i : vec[x]) q.push(i); } } for(int i = 0; i < Q; i++){ cin >> tmp1; cout << (poll[tmp1] ? "TUIHUOOOOOO\n" : "YA~~\n"); } } ``` ::: ## deque deque 念法(ㄉㄟˋㄎㄜˇ ) 中文叫雙向隊列 宣告寫法 ```cpp= deque<int> dq; ``` 簡單來講就是可以在首尾新增、刪除元素的vector 大致上跟vector差不多,所以就直接講用途 `dq.push_front()` 在最前面放入元素 `dq.push_back()` 在最後面放入元素 `dq.pop_front()` 移除前面的元素 `dq.pop_back()` 移除後面的元素 `dq.front()` 回傳最前面的元素 `dq.back()` 回傳最後面的元素 `dq.size()` 回傳deque的大小 `dq.empty()` 回傳一個bool,如果為空true,否則false 同樣的也可以像vector一樣用emplace來壓常數 跟vector一樣也是隨機存取迭代器,所以也可以把兩個迭代器指標做運算 ### deque常見用途 sliding window DP的單調對列優化 ### 注意事項 deque雖然比vector的功能還要來的多,但是deque卻比vector還要慢很多,所以能用vector達成的盡量用vector來達成。 後面提到的適配器(Container Adapters)很多內建也都是deque,所以我們可以把那適配器容器改成vector來增加效率。 ### 例題 ## list STL的list是由一種叫連結串列(linked-list)來包裝 但是list其實在競程中沒有很常用到 而且在解題時我們也可以用陣列+struct來取代list 如果想要知道更多list的內容的話可以看[這篇文章](https://hackmd.io/@Zero871015/H12vTu8aX?type=view) ### 例題 ## stack 一種先進後出的資料結構,由資料的同一端開口加入資料(push)或刪除資料(pop)。 ### 實作 ```cpp= ``` 生活上的例子:賣場的推車(由同個方向加入推車或拿走推車) ## queue 與stack語法類似,最大的不同是stack是後進先出,而queue則是先進先出 ## priority_queue ## set ## map ## pair ## bitset