STL容器學習總結

 本文主要討論C++標準庫中的順序容器及相應的容器適配器,這些內容主要涉及順序容器類型:vector、list、deque,順序容器適配器類型:stack、queue、priority_queue。
      標準庫中的容器分爲順序容器和關聯容器。順序容器(sequential container)內的元素按其位置存儲和訪問,顧名思義,這些內部元素是順序存放的;順序容器內的元素排列次序與元素值無關,而是由元素添加到容器裏的次序決定。而關聯容器的元素按鍵(key)排序。
 
      容器類共享部分公共接口。標準庫定義的三種順序容器類型:vector、list、deque(double-ended queue的縮寫,發音爲“deck”),它們的差別僅在訪問元素的方式,以及添加或刪除元素相關操作的代價。順序容器適配器包括:stack、queue和priority_queue。容器只定義了少量操作,大多數操作由算法庫提供。如果兩個容器提供了相同的操作,則它們的接口(函數名和參數個數)應該相同。 

標準容器類 說明
順序性容器
vector 從後面快速的插入與刪除,直接訪問任何元素
deque 從前面或後面快速的插入與刪除,直接訪問任何元素
list 雙鏈表,從任何地方快速插入與刪除
關聯容器
set 快速查找,不允許重複值
multiset 快速查找,允許重複值
map 一對多映射,基於關鍵字快速查找,不允許重複值
multimap 一對多映射,基於關鍵字快速查找,允許重複值
容器適配器
stack 後進先出
queue 先進先出
priority_queue 最高優先級元素總是第一個出列

      容器類型

 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
 >, >=, <, <=  比較迭代器的位置關係;只適用於vector和deque
 
   關係操作符只適用於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指向容器內最後一個元素,則返回的迭代器指向容器的超出末端的下一個位置;如果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<bool> 與bitset<> ,前面的可以動態改變長度。
priority_queue: 插入的元素就有優先級順序,top出來的就是優先級最高的了
valarray 專門進行數值計算的,增加特殊的數學函數。

   一些容器選用法則:
   1)如果程序要求隨機訪問元素,則應使用vector或deque容器;
   2)如果程序必須在容器的中間位置插入或刪除元素,則應採用list容器;
   3)如果程序不是在容器的中間位置,而是在容器首部或尾部插入或刪除元素,則應採用deque容器;
   4)如果只需要在讀取輸入時在容器的中間位置插入元素,然後需要隨機訪問元素,則可以在輸入時將元素讀入到一個list容器中,然後對容器排序,再將排序後的list容器複製到vector容器中。
   5)如果程序既需要隨機訪問,又需要在容器的中間位置插入或刪除元素,此時應當權衡哪種操作的影響較大,從而決定選擇list容器還是vector或deque容器。注:此時若選擇使用vector或deque容器,可以考慮只使用它們和list容器所共有的操作,比如使用迭代器而不是下標,避免隨機訪問元素等,這樣在必要時,可以很方便地將程序改寫爲使用list容器。
容器適配器
     適配器(adaptor)是標準庫中通用的概念,包括容器適配器、迭代器適配器和函數適配器。本質上,適配器是使一事物的行爲類似於另一事物的行爲的一種機制。容器適配器讓一種已存在的容器類型採用另一種不同的抽象類型的工作方式實現,只是發生了接口轉換而已。
     標準庫提供了三種順序容器適配器:queue, priority_queue和stack。
     所有適配器都定義了兩個構造函數:默認構造函數用於創建空對象,而帶一個容器參數的構造函數將參數容器的副本作爲其基礎值。
     默認的stack和queue都基於deque容器實現,而priority_queue則在vector容器上實現。在創建適配器時,通過將一個順序容器指定爲適配器的第二個類型參數,可覆蓋其關聯的基礎容器類型。例如:
    stack<int, vector<int> > int_stack;  // 此時,int-stack棧是基於vector實現
    對於給定的適配器,其關聯的容器必須滿足一定的約束條件。stack適配器所關聯的基本容器可以是任意一種順序容器類型,因爲這些容器類型都提供了push_back、pop_back和back操作;queue適配器要求其關聯的基礎容器必須提供pop_front操作,因此其不能建立在vector容器上;priority_queue適配器要求提供隨機訪問功能,因此不能建立在list容器上。
 
   兩個相同類型的適配器可以做==, !=, <, >, <=, >=這些關係運算,只要其基本元素類型支持==和<兩個操作即可。這與容器大小比較原則一致。
這與容器大小比較原則一致。
 s.empty()  如果棧爲這人,則true;否則返回false
 s.size()  返回棧中元素的個數
 s.pop()  刪除棧頂元素,但不返回其值
 s.top()  返回棧頂元素的值,但不刪除該元素
 s.push(item)  在棧項壓入新元素
 
隊列和優先級隊列
   標準庫隊列使用了先進先出(FIFO)的存儲和檢索策略,進入隊列的元素被放置在尾部,下一個被取出的元素則取自隊列的首部。
   priority_queue默認使用元素類型的 < 操作符來確定它們之間的優先級關係,用戶也可以定義自己的優先級關係。在優先級隊列中,新元素被放置在比它優先級低的元素的前面。
 
 q.empty()  如果隊列爲空,則返回true;否則返回false
 q.size()  返回隊列中元素的個數
 q.pop()  刪除隊首元素,但不返回其值
 q.front()  返回隊首元素的值,但不刪除該元素
 (注:該操作只適用於隊列
 q.back()  返回隊尾元素的值,但不刪除該元素
 (注:該操作只適用於隊列
 q.top()

 返回具有最高優先級的元素值,但不刪除該元素
 (注:該操作只適用於優先級隊列。MSDN也爲queue提供了該操作

 q.push(item)  對於queue,在隊尾壓入一個新元素;
 對於priority_queue,在基於優先級的適當位置插入新元素
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章