C++ STL教學(11)容器總結

2020-08-12 16:18:01

本文主要討論C++標準庫中的容器,這些內容主要涉及順序容器型別:vector、list、deque,關聯容器:set、multiset 。

標準庫中的容器分爲順序容器和關聯容器。順序容器(sequential container)內的元素按其位置儲存和存取,顧名思義,這些內部元素是順序存放的;順序容器內的元素排列次序與元素值無關,而是由元素新增到容器裡的次序決定。而關聯容器的元素按鍵(key)排序。

容器類共用部分公共介面。標準庫定義的三種順序容器型別:vector、list、deque(double-ended queue的縮寫,發音爲「deck」),它們的差別僅在存取元素的方式,以及新增或刪除元素相關操作的代價。順序容器適配器包括:stack、queue和priority_queue。容器只定義了少量操作,大多數操作由演算法庫提供。如果兩個容器提供了相同的操作,則它們的介面(函數名和參數個數)應該相同。

標準容器類 說明
順序性容器
vector 從後面快速的插入與刪除,直接存取任何元素
deque 從前面或後面快速的插入與刪除,直接存取任何元素
list 雙鏈表,從任何地方快速插入與刪除
關聯容器
set 快速查詢,不允許重複值
multiset 快速查詢,允許重複值
map 一對多對映,基於關鍵字快速查詢,不允許重複值
multimap 一對多對映,基於關鍵字快速查詢,允許重複值

容器型別

vector 容器,支援快速隨機存取(連續儲存)
list 鏈表,支援快速插入/刪除
deque 雙端佇列,支援隨機存取(連續儲存),兩端能快速插入和刪除
stack
queue 佇列
priority_queue 優先順序佇列

容器迭代器

下表爲迭代器爲所有容器型別所提供的運算:

*iter 返回型別iter所指向的元素的參照
iter->mem 對iter進行解除參照,並取得指定成員
++iter 給iter加1,使其指向容器中下一個元素
iter++
–iter 給iter減1,使其指向容器中前一個元素
iter–
iter1 == iter2 當兩個迭代器指向同一個容器中的同一元素,或者當它們都指向
iter1 != iter2 同一個容器的超出末端的下一個位置時,兩個迭代器相等。

vector和deque容器的迭代器提供了額外的運算:迭代器的算術運算和另一些關係運算,如下表所示:

iter + n 在迭代器上加(減)整數值,將產生指向容器中前面(後面)第n個元素的迭代器;
iter - n 新計算出來的迭代器必須指向容器中的元素或超出容器末端的下一位置。
iter1 += iter2 複合運算:先加(減),再賦值
iter1 -= iter2
iter1 - iter2
>, >=, <, <= 比較迭代器的位置關係

關係操作符只適用於vector和deque容器,這是因爲只有這兩種容器爲其元素提供快速、隨機的存取。它們確保可根據元素位置直接有效地存取指定的容器元素。這兩種容器都支援通過元素位置實現的隨機存取,因此它們的迭代器可以有效地實現算術和關係運算。

迭代器範圍:[first, last)是一個左閉合區間,表示範圍從first開始,到last結束,但不包括last。注意:如果first不等於last,則對first反覆 反復做自增運算必須能夠到達last;否則,即last位於first之前,則將發生未定義行爲。

迭代器範圍使用左閉合的意義:因爲這樣可以統一表示空集,就無需特別處理。

另外,使用迭代器時,要特別留意迭代器的可能的失效問題。

存取元素

back() 返回容器的最後一個元素的參照。如果容器爲空,則該操作未定義
front() 返回容器的第一個元素的參照。如果容器爲空,則該操作未定義
c[n] 返回下標爲n的元素的參照;如果n<0 or n>=size(),則該操作未定義 (注:只適用於vector和deque容器)
at[n] 返回下標爲n的元素的參照;如果下標無效,則拋出異常out_of_range異常 (注:只適用於vector和deque容器)

刪除元素

erase§ 刪除迭代器p所指向的元素。返回一個迭代器,它指向被刪除的元素後面的元素。如果p指向容器內最後一個元素,則返回的迭代器指向容器的超出末端的下一個位置;如果p本身就是指向超出末端的下一個位置的迭代器,則該函數未定義
erase(b, e) 刪除[b, e)內的所有元素。返回一個迭代器,它指向被刪除元素段後面的元素。如果e本身就是指向超出末端的下一個位置的迭代器,則返回的迭代器也指向超出末端的下一個位置。
clear() 刪除容器內的所有元素,返回void
pop_back() 刪除容器內的最後一個元素,返回void。如果容器爲空,則該操作未定義。
pop_front() 刪除容器內的第一個元素,返回void。如果c爲空容器,則該操作未定義 (注:只適用於list和deque容器)

賦值與swap

c1 = c2 刪除容器c1的所有元素,然後將c2的元素複製給c1。c1和c2的型別必須相同。
c1.swap(c2) 交換內容:呼叫該函數後,c1中存放的是c2原來的元素,c2中存放的是c1原來的元素。c1和c2的型別必須相同。該函數的執行速度通常要比將c2的元素複製到c1的操作快。
c.assign(b, e) 重新設定c的元素:將迭代器b和e標記的範圍內所有的元素複製到c中。b和e必須不是指向c中元素的迭代器。
c.assign(n, t) 將容器c重新設定爲儲存n個值爲t的元素。

注意:assign操作首先刪除容器內所有的元素,再將參數所指定的新元素插入到容器中。

swap操作不會刪除或插入任何元素,而且保證在常數時間內實現交換。由於容器內沒有移動任何元素,因此迭代器不會失效。但要注意這些迭代器指向了另一個容器中的元素。

容器的選用

vector和deque容器提供了對元素的快速存取,但付出的代價是,在容器的任意位置插入或刪除元素,比在容器尾部插入和刪除的開銷更大,因爲要保證其連續儲存,需要移動元素;list型別在任何位置都能快速插入和刪除,因爲不需要保證連續儲存,但付出的代價是元素的隨機存取開銷較大。特徵如下:

1)與vector容器一樣,在deque容器的中間insert或erase元素效率比較低;

2)不同於vector容器,deque容器提供高效地在其首部實現insert和erase的操作,就像在尾部一樣;

3)與vector容器一樣而不同於list容器的是,deque容器支援對所有元素的隨機存取。

4)在deque容器首部或尾部刪除元素則只會使指向被刪除元素的迭代器失效。在deque容器的任何其他位置的插入和刪除操作將使指向該容器元素的所有迭代器都失效。

容器的比較

vector (連續的空間儲存,可以使用[]操作符)快速的存取隨機的元素,快速的在末尾插入元素,但是在序列中間歲間的插入,刪除元素要慢,而且如果一開始分配的空間不夠的話,有一個重新分配更大空間,然後拷貝的效能開銷。
deque (小片的連續,小片間用鏈表相連,實際上內部有一個map的指針,因爲知道型別,所以還是可以使用[],只是速度沒有vector快)快速的存取隨機的元素,快速的在開始和末尾插入元素,隨機的插入,刪除元素要慢,空間的重新分配要比vector快,重新分配空間後,原有的元素不需要拷貝。對deque的排序操作,可將deque先複製到vector,排序後在複製回deque。
list (每個元素間用鏈表相連)存取隨機元素不如vector快,隨機的插入元素比vector快,對每個元素分配空間,所以不存在空間不夠,重新分配的情況。

set:內部元素唯一,用一棵平衡樹結構來儲存,因此遍歷的時候就排序了,查詢也比較快的哦。
map :一對一的對映的結合,key不能重複。
stack :適配器,必須結合其他的容器使用,stl中預設的內部容器是deque。先進後出,只有一個出口,不允許遍歷。
queue: 是受限制的deque,內部容器一般使用list較簡單。先進先出,不允許遍歷。
vector 與bitset<> ,前面的可以動態改變長度。
priority_queue: 插入的元素就有優先順序順序,top出來的就是優先順序最高的了
valarray 專門進行數值計算的,增加特殊的數學函數。

一些容器選用法則:

1)****如果程式要求隨機存取元素,則應使用vector或deque容器;****

2)****如果程式必須在容器的中間位置插入或刪除元素,則應採用list容器;****

3)如果程式不是在容器的中間位置,而是在容器首部或尾部插入或刪除元素,則應採用deque容器;

4)如果只需要在讀取輸入時在容器的中間位置插入元素,然後需要隨機存取元素,則可以在輸入時將元素讀入到一個list容器中,然後對容器排序,再將排序後的list容器複製到vector容器中。

5)如果程式既需要隨機存取,又需要在容器的中間位置插入或刪除元素,此時應當權衡哪種操作的影響較大,從而決定選擇list容器還是vector或deque容器。注:此時若選擇使用vector或deque容器,可以考慮只使用它們和list容器所共有的操作,比如使用迭代器而不是下標,避免隨機存取元素等,這樣在必要時,可以很方便地將程式改寫爲使用list容器。