C++ 刷題利器 - STL (Standard Template Library) | LeetCode
甚麼是 STL (Standard Template Library)?
在 C++ 中,STL即為一群容器(Container)的集合,不同容器可以實現不同的資料結構,其實是大量使用了 C++中的 Template 來去實現的。
透過該資料結構實現出演算法,STL還提供對於容器的操作,不同資料結構的容器分別也有不同的操作方式。
Template 簡單來說就是定義好結構,並且可用於不同的資料型別上,例如我寫了一個陣列的Template,但它可以是 int[], float[], double [] 或者是 char []
template <typename T>
通常可以這樣來定義 Template ,通常使指角括號內的東西,可以想像成compiler 幫你做複製貼上
STL 元件
主要有6大個元件:
- 容器 (Container)
- 演算法 (Algorithm)
- 迭代器(Iterator)
- 仿函數(Function object)
- 適配器(Adaptor)
- 空間配置器(allocator)
對於刷題,最需要focus的重點會是容器和迭代器,另外還有algorithm,例如
sort
,通常以 function 的形式存在,多是一次性的計算,但Leetcode中大多需要自己實作,因此不細談
STL 容器
container 通常是屬於資料結構的部份,以變數的方式存在,可持續地互動與維護資料
其中Leetcode 能使用的容器類型有: Vector, List, Stack, Queue, PriorityQueue(Binary Heap), Set/MultiSet, Unorder Set, Map/MultiMap, Unorder Map
STL 迭代器
iterator 用來依序拜訪一個 container 的所有元素,也用以指稱 container 中的特定元素,可表達搜尋結果或者範圍的端點
下面是範例
1 |
|
取自: https://hackmd.io/@sa072686/cp/%2F%40sa072686%2FS11uDpiuH
可以看到在for
迴圈內可以使用 vector<int>::iterator
來去宣告一個迭代器 it
,並且它會從 vector 的首端開始,若不等於尾端則+1;
這裡可以觀察到幾個重點:
- iterator 的宣告方式是
容器類型::iterator
- iterator 支援
++
或--
運算 - 支援 iterator 的 container,基本都能用
.begin()
取得最初的元素,.end()
取得最後的元素的再下一個位置,是個不存在的空位,符合 STL 左包含、右不包含的規則 - 透過取值運算子
*
可以取得iterator目前位址的值
大多數的時候,把它理解為指標是沒有問題的(指標是迭代器的一個特例,它也屬於迭代器)
auto 自動型別判別
上面的範例會發現一件事,就是宣告 iterator的時候真的長度很長,因此有個偷懶作法就是透過 auto
來讓compiler來進行自動型別判別,compiler 會依據初始值的型別來決定變數的型別
可以將for迴圈改成下面這樣
1 | vector<int> v; |
對於 vector
來說,這算是一種random access 的 iterator,甚麼事random access?
Random Access
若一個 container 可以在 $O(1)$ 複雜度進行存取,則它就具有 random access 的特性
如果是為了刷題,會使用到的迭代器也隨著容器種類有所不同,可以參考下面表格
Iterator Type | Description | Container |
---|---|---|
Bidirectional iterator | Read and Writes forward and backward | list,set,multiset,map,multimap |
Random access iterator | Read and Write with random access | vector,deque,array,string |
常見 STL 容器
Vector
可以想成是一個動態的陣列
- 可以在 $O(1)$ 時間內存取跟修改元素
- 在集合的中間修改元素會是 $O(n)$,一般建議將要刪除的值先swap到最後,然後再刪除。
1 |
Vector 初始化
1 | // 宣告一個 int vector |
Vector 常見操作
1 | v.empty(); |
最後面的 v.erase()
,其實如果不在乎順序的話可以改成使用
1 | swap(v[n], v.back()); // 交換n和最後一個位置 |
剩下還有一些操作
1 | // Traversal |
Traversal 還可以寫成下面的形式,但這個就會提到 Iterator,稍後會提到。
1 | for(auto it = v.begin(); it != v.end(); ++it) { |
List
移動或是刪除等操作都是 $O(1)$
1 |
1 | // delcare list container |
常見操作:
1 | l.empty(); |
將一個list中的值移到另一個list中
1 | l.splice(iterator pos, list& x); // 把x所有的element接到l中pos的位置。 |
Stack
特性: LIFO(Last-In-First-Out), Stack是object的有限序列,並滿足序列中被刪除、檢索和修改的項只能是最近插入序列的obj
1 |
|
Queue
特性: FIFO(First-In-First-Out),所以插入只可以在尾部進行,刪除、檢索和修改只允許從頭部進行。
1 |
|
PriorityQueue
其實就是 heap 結構,常用於Dijkstra 演算法中
1 |
|
要注意的是,PQ 的 pop
跟 push
複雜度會是 $O(Log n)$
make heap 操作
有個省空間的方法,就是透過 make_heap
來讓 vector 變成 heap,這樣空間複雜度會變成 $O(1)$
1 | vector<int> nums{1, 3, 5, 2, 4, 6}; |
Set 和 MultiSet
Set 就是集合
預設set會從小到大排序,set容器裡面的元素是唯一的,具有不重複的特性
而 multiset可以允許元素重複。unordered_set 不排序的set。unordered_multiset 不排序的multiset。
1 |
|
Map 和 MulitMap
Map 就像是一個對應表,使用key-value pair 來去實現一對一的映射關係
- 預設也是從小到大排序
- multimap 允許多個相同的key-value pair。
- unordered_map 不排序的map。
- unordered_multimap 不排序的multimap。
簡單範例:
1 |
|
1 |
|
map 的底層實現是用紅黑樹,因此它的對應都是按照key去做排序的,因此插入,查找,刪除等操作的複雜度都是 $O(Log n)$
unordered_map 的底層實現則是用hash table,是無序的,因此複雜度會是 $O(1)$
使用unordered_map的時候,根據key產生出來的hash來查找value。既然是hash就會有碰撞問題
As we know a Bucket is a slot in the container’s internal hash table to which all the element are assigned based on the hash value of their key . Buckets are numbered from 0 to bucket_count.
如果增加的數目超出bucket_count,map就會自動變大bucket_slot,並且重新計算所有item的hash。
When the Load Factor(load_factor) reaches a certain threshold, the container increases the number of buckets and rehashes the map.
使用以下的程式碼,就是放大bucket到n,並且重新計算hash table。
1 | m.refresh(n); |
如果我們事先知道大小可以使用以下function直接保留bucket到n,避免超出threshold需要放大container。因為事先保留了n個bucket也可以避免hash collision。
1 | m.reserve(n); |
參考
[1] https://hackmd.io/@sa072686/cp/%2F%40sa072686%2FS11uDpiuH
[2] https://jasonblog.github.io/note/c++/stl_rong_qi_4e0029_-_ji_ben_jie_shao.html
[3] https://hackmd.io/@meyr543/BkgMaiV6Y#Vector
[4] https://blog.csdn.net/weixin_42292229/article/details/125523668
[5] https://ikaminyou.medium.com/leetcode-%E5%88%B71500%E9%A1%8C%E5%BF%83%E8%B7%AF%E6%AD%B7%E7%A8%8B-8614284f03da