搞定技術面試:那些你可能不知道的 vector 和 array 的區別
最近幾年,計算機工作越發難找,你必須比其他人瞭解的更多,纔能有更多的機會找到一個更好的工作。
C++ 標準庫(STL)是很多C++面試中都會問到的問題,很多很多問題會關於 Vector 的空間分配、動態增長之類的問題,那麼你瞭解 STL 中那些順序容器的區別與聯繫嗎?
你知道在什麼情況選用什麼容器嗎?
先說結論,一般情況選擇 vector
是很好的選擇,如果你的程序目標有如下特點時,纔可能需要換用別的容器:
- 有很多小元素、空間額外開銷比較重要,不要使用 list 和 forward_list;
- 程序要求隨機訪問,選擇 vector 和 deque;
- 程序經常在中間插入元素,可以選擇 list 和 forward_list;
- 程序只在頭尾插入元素,不常在中間插入元素,選擇 deque;
- 程序一開始插入時會在中間插入元素,但之後的使用過程中基本不在中間插入元素,先用 list 處理輸入場景,再用 vector 拷貝 list 的數據,處理後續使用。
整體介紹
所有的順序容器都具有可以快速按順序訪問元素的能力,但每種容器在具體的實現上又有些區別。
順序容器類型 | 含義 | 特點 |
---|---|---|
vector | 可變大小數組 | 隨機訪問快、尾部插入元素快、其他位置插入、刪除元素慢 |
deque | 雙端隊列 | 隨機訪問快、頭尾插入元素快 |
list | 雙向鏈表 | 只支持雙向順序訪問、在任何位置插入、刪除元素都很快 |
forward_list | 單向鏈表 | 只支持從前往後的單向順序訪問、在任何位置插入刪除元素都很快 |
array | 固定大小數組 | 隨機訪問快、容器大小固定不支持插入刪除元素 |
string | 字符串,類似vector,專門保存字符、隨機訪問快、在尾部插入刪除元素快 |
按類型介紹
vector string
vector 和 string 將元素保存在連續的內存空間中,分配一段連續的內存空間進行存儲,其迭代器採用 C++ 指針即可,因此其支持隨機訪問和存儲,支持下標操作符,節省空間。但是其在分配的內存不夠的情況下,需要對容器整體進行重新分配、拷貝和釋放等操作(空間的動態增長),而且在 vector 和 string 中間插入或刪除元素效率很低。
vector的內存分配實現原理:STL內部實現時,首先分配一個非常大的內存空間預備進行存儲,即capacity()函數返回的大小,當超過此分配的空間時再整體重新放分配一塊內存存儲(VS6.0是兩倍,VS2005是1.5倍),所以這給人以vector可以不指定vector即一個連續內存的大小的感覺。通常此默認的內存分配能完成大部分情況下的存儲。
擴充空間(不論多大)都應該這樣做:
- 配置一塊新空間
- 將舊元素一一搬往新址
- 把原來的空間釋放還給系統
list forward_list
list 和 forward_list 是以節點形式來存放數據,使用的是非連續的內存空間來存放數據,因此,在其內部插入和刪除元素的時間複雜度都是O(1),但是其不支持隨機訪問和存取,不支持下標;因爲每個結點需要額外空間存儲連接的情況,他們比 vector 佔用的內存要多很多。
list 是非連續存儲結構,具有雙鏈表結構,每個元素維護一對前向和後向指針,因此支持前向/後向遍歷。支持高效的隨機插入/刪除操作,但隨機訪問效率低下,且由於需要額外維護指針,開銷也比較大。每一個結點都包括一個信息快Info、一個前驅指針Pre、一個後驅指針Post。可以不分配必須的內存大小方便的進行添加和刪除操作。使用的是非連續的內存空間進行存儲。
forward_list 底層實現上是單鏈表,且實質上無任何多餘開銷,與 list 相比,此容器在不需要雙向迭代時提供更有效地利用空間的存儲。在鏈表內或跨數個鏈表添加、移除和移動元素,不會非法化當前指代鏈表中其他元素的迭代器。forward_list 的迭代器不支持iter--
操作,即不支持反向迭代;同時 forward_list 也不支持size()
操作。
deque
vector是一個單向開口的容器,deque則是一個雙向開口的容器,所謂雙向開口就是再頭尾兩端均可以做元素的插入和刪除操作。
deque 在空間增長上與 vector 不同,它是動態的以分段的連續空間組合而成,隨時可以增加一段空間連接起來。
因此,爲了管理多段連接,deque有中控器的概念,此處實現上詳細情況的請參見《STL源碼剖析》
array
array 與內置數組類似,大小是固定的,因此不支持增加元素、刪除元素以及改變容器大小的功能。
在使用 array 時,必須同時指定元素類型和大小array<int,20>
此容器是一個聚合類型,其語義等同於保有一個 C 風格數組 T[N] 作爲其唯一非靜態數據成員的結構體。該結構體結合了 C 風格數組的性能、可訪問性與容器的優點,比如可獲取大小、支持賦值、隨機訪問迭代器等。
迭代器 Iterator
迭代器的範圍都是左開右閉,理解爲數學上的區間則是 [xx.begin(), xx.end())
,左邊是可以達到的,右邊是達不到的。