容器可以分為三類:序列式容器、關聯容器、無序容器,此外還有一些容器介面卡。
array
與vector
:array
與c中的陣列類似,是一種大小固定的儲存連續的容器;vector
也是儲存連續的,但它的長度可以動態調整。相對於陣列型別,這兩種容器更為安全。由於array
與vector
都是連續儲存的容器,能夠高效地利用索引訪問元素,但在中間部分插入/刪除元素比較低效。
deque
:一種雙端佇列,也是一種連續儲存的容器,能夠高效地在首尾部新增/刪除元素,但在中間部分插入/刪除元素比較低效。
list
與forward_list
:list
是由雙向鍊錶實現的,forward_list
是由單向鍊錶實現的,因此這兩種容器能夠高效地插入/刪除元素,但很難進行隨機訪問。
map
:map
型別儲存鍵值對,通常是以二叉樹實現的,預設以 key 作為比較的依據。map
中的 key 不允許重複,而multimap
與map
的唯一的區別是元素可以重複。
set
:set
型別只儲存鍵值,其它特性與map
相同,multiset
與set
的唯一區別是元素可以重複。
由於關聯容器的底層實現是二叉樹(紅黑樹),因此預設情況下關聯容器中的 keys 是有序的。如果使用關聯容器時對排序沒有需求,可以選擇下面的無序容器。
關聯容器中的四種型別都有對應的無序版本:unordered_map
,unordered_multimap
,unordered_set
,unordered_multiset
;無序容器通常是以雜湊表實現的。
stack
預設的容器是deque
,queue
預設的容器是deque
,priority_queue
與queue
功能類似,但是會按照壓入元素的值排序,彈出值最大的元素。預設使用的容器是vector
。
根據《c++標準庫》中的描述,預設情況下應該選擇vector
,如果需要經常在首尾部插入或移除元素,應該選擇deque
,如果需要經常在中間部分插入或移除元素,應該選擇list
。
如果需要經常查詢元素,應該選擇unordered_set
,如果需要使用鍵值對,應該選擇unordered_map
,如果在上述兩種場景下,需要滿足有序,應該選擇set
/map
。
容器實現了大量的功能,以成員函式的形式提供這些功能,這裡不在贅述,可以檢視 c++ reference / container。但是僅僅閱讀文件可能無法弄清楚這些成員函式的具體表現,需要在實踐中多累積經驗。
下面是一些我認為靈活使用的方法。
pair 與 tuple
儘管pair
提供了first
,second
來訪問元素,但是還可以使用get()
函式來訪問pair
,tuple
, 甚至array
的值,並且該函式還可以作為左值使用get<1>(p) = 10
。
pair
,tuple
都提供來比較的能力,按照先比較第乙個元素,相等時再比較第二個元素,……,以這樣的順序進行比較。
array
array.data()
會返回指向首元素的指標,這樣就可以像使用陣列一樣處理 array 的資料。如:
arraya ;
int* p = a.data();
for (int i = 0; i < 10; ++i)
vector
也提供了這個功能。
vector 與 deque
vector
與deque
非常相似,只有在雙端插入移除元素的需求時才會使用deque
。儘管vector
也提供來訪問首個元素的成員函式front
,但是沒有像deque
一樣提供插入移除首個元素的成員函式。
借助 initlist 來實現初始化乙個值的容器。
// 如果想初始化乙個 大小為 n 的 vector / deque
vectorvec(n);
// 如果想初始化乙個 第乙個元素為 n 的 vector / deque
vectorvec();
list 與 forward_list
相比與前面的vector
與deque
,list
與forward_list
增加來很多不同的能力:排序 sort,逆序 reverse,去重 unique,合併 merge,移動元素 splice,去除元素 remove 等。
forward_list
沒有實現size()
,原因可能是由於這種容器的設計目的是快速的插入,每次插入操作都去維護乙個儲存 size 的變數顯然會降低效率;而每次獲得 size 都遍歷一遍鍊錶的實現也是低效的,因為人們容易濫用size()
成員函式。索性就沒有實現,而且對於使用者而言也不難自己實現。
map 與 multimap
map
中的元素是有序的,在map
模板中,除前兩個引數用來指定 key 與 value 的型別外,還可以提供第三個引數,用於指定排序時使用的比較方法。因此可以有兩種方式來改變預設的排序規則:1) 指定map
模板中的排序引數;2) 修改所使用型別的比較函式。
預設的排序準則是std::less<>
,如果像降序排列,可以使用std::greater<>
,尖括號內為 key 的型別。或者想使用自定義的排序準則,可以自定義乙個比較的函式:
struct strlencmp
};mapm;
儘管使用這樣自定義的函式甚至可以實現以 value 值進行排序,但是map
中只能保證 key 值唯一,不能保證 value 值唯一,因此使用 value 作為排序依據得到的排序結果無法保證。如果真的需要使用 value 排序,可以將 pair 放入乙個 vector 中進行排序。
set 與 multiset
set
與map
同理,也可以設定排序準則。
unordered 容器
unordered 容器可以在模板引數中指定乙個 hash 函式,也可以在模板引數中指定乙個用於比較相等的函式,對於小型程式用的的概率不是很大,使用預設的引數就好。
補充:bitset
bitset
用來儲存一串布林值,使用vector
也可以。具體的用法:
bitset<4> flag("1100");
bitset<16> flags(0xffff);
這種工具提供了下標訪問,翻轉 flip,以及用於檢查全部位的 any all。
列表初始化為 c++ 提供來統一的初始化方式。不管是基本型別,還是容器,以及自定義的型別,都可以使用列表初始化。在自定義型別中的建構函式中使用初始化列表,那麼就可以使用列表初始化來進行,這兩個是對應的。
P03 多重揹包問題
有n種物品和乙個容量為v的揹包。第i種物品最多有n i 件可用,每件費用是c i 價值是w i 求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。這題目和完全揹包問題很類似。基本的方程只需將完全揹包問題的方程略微一改即可,因為對於第i種物品有n i 1種策略 取0件,取1件...
P03 多重揹包問題
有n種物品和乙個容量為v的揹包。第i種物品最多有n i 件可用,每件費用是c i 價值是w i 求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。這題目和完全揹包問題很類似。基本的方程只需將完全揹包問題的方程略微一改即可,因為對於第i種物品有n i 1種策略 取0件,取1件...
P03 多重揹包問題
有n種物品和乙個容量為v的揹包。第i種物品最多有n i 件可用,每件費用是c i 價值是w i 求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。這題目和完全揹包問題很類似。基本的方程只需將完全揹包問題的方程略微一改即可,因為對於第i種物品有n i 1種策略 取0件,取1件...