1. 程式人生 > 實用技巧 >STL

STL

STL概況

面試題

STL常用的容器有哪些以及各自的特點是什麼?

1.vector:底層資料結構為陣列 ,支援快速隨機訪問。

2.list:底層資料結構為雙向連結串列,支援快速增刪。

3.deque:底層資料結構為一箇中央控制器和多個緩衝區,支援首尾(中間不能)快速增刪,也支援隨機訪問。

4.stack:底層一般用23實現,封閉頭部即可,不用vector的原因應該是容量大小有限制,擴容耗時

5.queue:底層一般用23實現,封閉頭部即可,不用vector的原因應該是容量大小有限制,擴容耗時(stack和queue其實是介面卡,而不叫容器,因為是對容器的再封裝)

6.priority_queue:的底層資料結構一般為vector為底層容器,堆heap為處理規則來管理底層容器實現

7.
set:底層資料結構為紅黑樹,有序,不重複。 8.multiset:底層資料結構為紅黑樹,有序,可重複。 9.map:底層資料結構為紅黑樹,有序,不重複。 10.multimap:底層資料結構為紅黑樹,有序,可重複。 11.hash_set:底層資料結構為hash表,無序,不重複。 12.hash_multiset:底層資料結構為hash表,無序,可重複 。 13.hash_map :底層資料結構為hash表,無序,不重複。 14.hash_multimap:底層資料結構為hash表,無序,可重複。

使用場景

1、如果你需要高效的隨機存取,而不在乎插入和刪除的效率,使用vector


2、如果你需要大量的插入和刪除,而不關心隨機存取,則應使用list
3、如果你需要隨機存取,而且關心兩端資料的插入和刪除,則應使用deque。

4、如果你要儲存一個數據字典,並要求方便地根據key找value,那麼map是較好的選擇

5、如果你要查詢一個元素是否在某集合記憶體中,則使用set儲存這個集合比較好

vector 和 list 的區別

1)vector, 連續儲存的容器,動態陣列,在堆上分配空間 ;

底層實現:陣列

如果沒有剩餘空間了,則會重新配置原有元素個數的兩倍空間,然後將原空間元素通過複製的方式初始化新空間,再向新空間增加元素

適用場景:經常隨機訪問,且不經常對非尾節點進行插入刪除。

2)list,動態連結串列,在堆上分配空間,每插入一個元素都會分配空間,每刪除一個元素都會釋放空間。

底層:雙向連結串列

訪問:隨機訪問效能很差,只能快速訪問頭尾節點

適用場景:經常插入刪除大量資料

3) vector在中間節點進行插入刪除會導致記憶體拷貝,list不會。

4) vector一次性分配好記憶體,不夠時才進行2倍擴容;list每次插入新節點都會進行記憶體申請。

5) vector擁有一段連續的記憶體空間,因此支援隨機訪問,如果需要高效的隨即訪問,而不在乎插入和刪除的效率,使用vector。

list擁有一段不連續的記憶體空間,如果需要高效的插入和刪除,而不關心隨機訪問,則應使用list。

vector每次insert或erase之後,以前儲存的iterator會不會失效?

理論上會失效,理論上每次insert或者erase之後,所有的迭代器就重新計算的,所以都可以看作會失效,原則上是不能使用過期的記憶體,但是vector一般底層是用陣列實現的,我們仔細考慮陣列的特性,不難得出另一個結論,insert時,假設insert位置在p,分兩種情況:

a) 容器還有空餘空間,不重新分配記憶體,那麼p之前的迭代器都有效,p之後的迭代器都失效

b) 容器重新分配了記憶體,那麼p之後的迭代器都無效,erase時,假設erase位置在p,則p之前的迭代器都有效並且p指向下一個元素位置(如果之前p在尾巴上,則p指向無效尾end),p之後的迭代器都無效

STL對於小記憶體塊請求與釋放的處理

  STL考慮到小型記憶體區塊的碎片問題,設計了雙層級配置器,第一級配置直接使用malloc()和free();第二級配置器則視情況採用不同的策略,當配置區大於128bytes時,直接呼叫第一級配置器;當配置區塊小於128bytes時,便不借助第一級配置器,而使用一個memory pool來實現。究竟是使用第一級配置器還是第二級配置器,由一個巨集定義來控制。SGI STL中預設使用第二級配置器。
  二級配置器會將任何小額區塊的記憶體需求量上調至8的倍數,(例如需求是30bytes,則自動調整為32bytes),並且在它內部會維護16個free-list, 各自管理大小分別為8, 16, 24,…,128bytes的小額區塊,這樣當有小額記憶體配置需求時,直接從對應的free list中拔出對應大小的記憶體(8的倍數);當客戶端歸還記憶體時,將根據歸還記憶體塊的大小,將需要歸還的記憶體插入到對應free list的最頂端。
小結:
STL中的記憶體分配器實際上是基於空閒列表(free list)的分配策略,最主要的特點是通過組織16個空閒列表,對小物件的分配做了優化。
1)小物件的快速分配和釋放。當一次性預先分配好一塊固定大小的記憶體池後,對小於128位元組的小塊記憶體分配和釋放的操作只是一些基本的指標操作,相比於直接呼叫malloc/free,開銷小。
2)避免記憶體碎片的產生。零亂的記憶體碎片不僅會浪費記憶體空間,而且會給OS的記憶體管理造成壓力。
3)儘可能最大化記憶體的利用率。當記憶體池尚有的空閒區域不足以分配所需的大小時,分配演算法會將其鏈入到對應的空閒列表中,然後會嘗試從空閒列表中尋找是否有合適大小的區域,
但是,這種記憶體分配器侷限於STL容器中使用,並不適合一個通用的記憶體分配。因為它要求在釋放一個記憶體塊時,必須提供這個記憶體塊的大小,以便確定回收到哪個free list中,而STL容器是知道它所需分配的物件大小的,比如上述:
stl::vectorarray;
array是知道它需要分配的物件大小為sizeof(int)。一個通用的記憶體分配器是不需要知道待釋放記憶體的大小的,類似於free(p)。

map 和 set 有什麼區別

1)map和set都是C++的關聯容器,其底層實現都是紅黑樹(RB-Tree)。

2)map中的元素是key-value(關鍵字—值)對:關鍵字起到索引的作用,值則表示與索引相關聯的資料;Set與之相對就是關鍵字的簡單集合,set中每個元素只包含一個關鍵字。

3)set的迭代器是const的,不允許修改元素的值;map允許修改value,但不允許修改key。

4) map支援下標操作,set不支援下標操作。map可以用key做下標。

unordered_map和map

內部實現機理

  • map: map內部實現了一個紅黑樹,該結構具有自動排序的功能,因此map內部的所有元素都是有序的,紅黑樹的每一個節點都代表著map的一個元素,因此,對於map進行的查詢,刪除,新增等一系列的操作都相當於是對紅黑樹進行這樣的操作,故紅黑樹的效率決定了map的效率。
  • unordered_map: unordered_map內部實現了一個雜湊表,因此其元素的排列順序是雜亂的,無序的

優缺點以及適用處

map優點:

  • 有序性,這是map結構最大的優點,其元素的有序性在很多應用中都會簡化很多的操作
  • 紅黑樹,內部實現一個紅黑書使得map的很多操作在的時間複雜度下就可以實現,因此效率非常的高
  • 對於那些有順序要求的問題,用map會更高效一些

map 缺點: 空間佔用率高,因為map內部實現了紅黑樹,雖然提高了執行效率,但是因為每一個節點都需要額外儲存父節點,孩子節點以及紅/黑性質,使得每一個節點都佔用大量的空間

unordered_map優點:

因為內部實現了雜湊表,因此其查詢速度非常的快

unordered_map 缺點:
對於查詢問題,unordered_map會更加高效一些,因此遇到查詢問題,常會考慮一下用unordered_map,
雜湊表的建立比較耗費時間

STL 中迭代器的作用,有指標為何還要迭代器

1) Iterator(迭代器)模式又稱Cursor(遊標)模式,用於提供一種方法順序訪問一個聚合物件中各個元素, 而又不需暴露該物件的內部表示

2) 迭代器不是指標,是類模板,表現的像指標。他只是模擬了指標的一些功能,通過過載了指標的一些操作符,->、*、++、--等,相當於一種智慧指標。

3) 迭代器產生原因:Iterator類的訪問方式就是把不同集合類的訪問邏輯抽象出來,使得不用暴露集合內部的結構而達到迴圈遍歷集合的效果。

STL 迭代器是怎麼刪除元素的呢

1) 對於序列容器vector,deque來說,使用erase(itertor)後,後邊的每個元素的迭代器都會失效,但是後邊每個元素都會往前移動一個位置,但是erase會返回下一個有效的迭代器;

2) 對於關聯容器map set來說,使用了erase(iterator)後,當前元素的迭代器失效,但是其結構是紅黑樹,刪除當前元素的,不會影響到下一個元素的迭代器,所以在呼叫erase之前,記錄下一個元素的迭代器即可。

3) 對於list來說,它使用了不連續分配的記憶體,並且它的erase方法也會返回下一個有效的iterator。

平衡二叉樹(AVL樹)和紅黑樹

1)平衡二叉樹又稱為AVL樹,是一種特殊的二叉排序樹。其左右子樹都是平衡二叉樹,且左右子樹高度之差的絕對值不超過1。

2)紅黑樹是一種二叉查詢樹,但在每個節點增加一個儲存位表示節點的顏色,可以是紅或黑(非紅即黑),紅黑樹是一種弱平衡二叉樹,相對於要求嚴格的AVL樹來說,它的旋轉次數少,所以對於搜尋,插入,刪除操作較多的情況下,通常使用紅黑樹。

3)所以紅黑樹在查詢,插入刪除的效能都是O(logn),且效能穩定,所以STL裡面很多結構包括map底層實現都是使用的紅黑樹。

雜湊表(hash表)

雜湊表的實現主要包括構造雜湊和處理雜湊衝突構造雜湊,主要包括直接地址法,除留餘數法

處理雜湊衝突:當雜湊表關鍵字集合很大時,關鍵字值不同的元素可能會對映到雜湊表的同一地址上,這樣的現象稱為雜湊衝突。常用的解決方法有:

1) 開放定址法,衝突時,用某種方法繼續探測雜湊表中的其他儲存單元,直到找到空位置為止。(如,線性探測,平方探測)

2) 再雜湊法:當發生衝突時,用另一個雜湊函式計算地址值,直到衝突不再發生。

3) 鏈地址法:將所有雜湊值相同的key通過連結串列儲存,key按順序插入連結串列中。

參考

Lucky&C++面試之STL

STL筆試面試題總結(乾貨)