C++ STL 總結

更新日期:2019-09-11

概述

在使用容器之前,您應該至少了解 C++ 的基礎知識,以及數據結構的基礎知識(文內就不描述數據結構的內容了)。文內默認 C++ 版本為 C++ 17

我們來看一下什么叫容器

容器
儲存一系列元素的對象。每一個容器都管理其元素的存儲空間并允許開發者通過迭代器和成員函數訪問其內部元素。 C++ 的 STL 容器 通常用于實現我們在程序中經常使用到的數據結構,例如

  • 隊列/雙端隊列
  • 鏈表
  • 關聯集合
  • 動態數組

C++Java 等靜態類型語言中,被儲存的對象必須是同一類型。

分類:

  • 序列容器:一種各元素之間有順序關系的線性表,是一種線性結構的可序群集。

    • 順序性容器中的每個元素均有固定的位置,除非用刪除或插入的操作改變這個位置。
    • 順序容器的元素排列次序與元素值無關,而是由元素添加到容器里的次序決定
  • 序列容器適配器

  • 關聯容器

    • 關聯式容器是非線性的樹結構,更準確的說是二叉樹結構。
    • 各元素之間沒有嚴格的物理上的順序關系,也就是說元素在容器中并沒有保存元素置入容器時的邏輯順序。
    • 但是關聯式容器提供了另一種根據元素特點排序的功能,這樣迭代器就能根據元素的特點“順序地”獲取元素。
    • 元素是有序的集合,默認在插入的時候按升序排列(set,multiset,map,multimap)!
  • 無序關聯容器

序列容器

序列容器用作以線性方式存儲同一類型對象的數據結構。

Vector

我們常常稱 vector 是動態數組,基于數組實現。其在內存中是一段連續的區域。何謂動態?動態就是說 vector 有自動的內存管理功能。可以動態的改變vector的長度,并隨著元素的增加與減小來自動改變數組大小,它提供了直接添加尾部元素或者刪除元素的方法!所以它的時間是固定的!然而,他要在頭部與中間插入或者刪除元素是線性的時間復雜度!

特點:他可以反轉序列,所以它可以反向遍歷可反轉序列!(基于他的rbegin,rend)

定義與初始化:

要調用頭文件

  #include<vector>

定義與初始化:

vector<int> v;//默認初始化
vector<int> v(v1);//用v1初始化v
vector<int> v(v1.begin(),v1.end());//用v1初始化v
vector<int> v(10);//定義一個大小為10的數組!
vector<int> v(10,1)//定義個全為1而且長度為10的數組

方法:

a.front(),a.rbegin()  //首元素
a.back(),a.rend()   //末尾元素
v.push_back()      //增
v.insert()   //由于insert重載方法比較多
  //  1.v.insert(p,t)//將t插到p的前面
  //  2.v.insert(p,n,t)//將n個t插入p之前
  //  3.v.insert(p,i.j)//將區間[i,j)的元素插入到p之前
v.pop_back()       //刪
v.erase(t,k)
  //  1.v.erase(t,k)//刪除他們之間的元素
  //  2.v.erase(p)//刪除p指向的元素
v.chear===v.erase(begin(),end());

遍歷

//下標法
for(int i = 0; i < v.size(); i++) {
  std::cout << v[i];
}

//迭代器法
for(vector<int>::const_iterator iterator = v.begin();
  iterator != v.end();
  iterator++) {
  std::cout << *iterator;
}

// 上述代碼在現代 C++ 中可以使用 auto 關鍵字
//迭代器法
for(auto iterator = v.begin();
  iterator != v.end();
  iterator++) {
  std::cout << *iterator;
}

// 甚至,可以使用范圍循環
for(auto it : v) {
  std::cout << *it;
}

Array

數組 Array 是靜態連續數組。他的長度固定,使用沒有調節大小的操作,但是他有一些有意義的成員函數,如operator[] 和at(),當然有很多STL算法用于array,如copy(),for_each()。我們后面會詳細介紹一些C++知識點!

/**
 * 導入頭文件
 */
#include <array>

deque

雙端隊列,他的實現類\似與vector,支持隨機訪問,但是它訪問首元素的插入(push_front())與刪除(pop_front())的時間是固定的!而且他的執行速度要比vector快很多!所以題目中有大量的操作發生在序列的起始位置與結尾處,我們就要考慮用deque!

調用頭文件:
 #include<deque>

初始化與定義已經在序列要求里面,而且方法與vector類似,只是多了push_front(), pop_front(),我們就不做過多的闡述

list

雙向鏈表,list在鏈表中的任意一個位置插入與刪除一個元素時間是固定的!但是他不能隨機訪問,優點是元素的快速插入與刪除!從容器中插入與刪除元素之后i,迭代器指向元素將不變,不會移動已有元素,只是修改鏈表信息。

#include<list>

我們來看一下他的鏈表獨有成員函數!

void sort()   //使用<運算符對鏈表進行排序,時間復雜度O(NlogN)
void merge(list<T,Alloc>&x)  //將x與調用鏈表合并,要求:兩個鏈表必須要已經排好序!元素將保存在調用鏈表中,x為空,這個時間復雜度為線性!
void remove(const T &val)//刪除val的所有實例
void splice(iterator pos,list<T,Alloc>x)//將鏈表x的內容加到pos的前面,時間復雜度為固定時間

void unique() //去重,線性時間

forword_list

參加noip的同學注意了!他是C++11新添加的容器類!它主要實現了單向鏈表,只需要正向迭代器,他是不可逆轉容器,他功能比較少,但是它比較簡單!

序列容器適配器

stack

適配器,它可以將任意類型的序列容器轉換為一個堆棧,一般使用deque作為支持的序列容器。元素只能后進先出(LIFO)。不能遍歷整個stack。他給vector提供了棧接口!
與queue類似,如果要使用棧中元素,先用top檢索,然后用pop將他從棧中刪除,這個太過于常用不介紹方法!

queuue

他是一個配適器類,ostream_iterator就是一個配適器,讓輸出流能夠使用迭代器接口,同樣它實現了隊列接口!它不僅不允許隨機訪問元素,而且還不能遍歷隊列!元素只能先進先出(FIFO).
方法:

bool empty()//判斷是否為空
front()//隊首元素的訪問
back()//隊尾元素的訪問
push(x)//隊尾插入x
pop()//刪除隊首元素

priority_queue

另一個配適器,他與queue基本一樣,但是他的最大元素被移動到隊首(生活不總是公平對,隊列也一樣),內部區別在于底層結構不一樣,他用的是vector,當然我們可以修改確定拿個元素放在隊首的比較方式!

priority_queue<int> X //大根堆,默認初始化

priority_queue<int, vector<int>, greater<int>> x  //小根堆,運用了預定義函數greater<int>!

以下內容摘自C++API:

包含priority_queue 的頭文件是 <queue>
priority_queue類的主要成員:
priority_queue();    //默認構造函數,生成一個空的排序隊列
priority_queue(const queue&);    //拷貝構造函數
priority_queue& operator=(const priority_queue &);    //賦值運算符重載
priority_queue 的私有成員:
value_type;   //priority_queue中存放的對象類型,它和priority_queue中的T類型相同
priority_queue(const Compare& comp);    //構造生成一個空的priority_queue對象,使用comp作為priority_queue的comparison
priority_queue(const value_type* first, const value_type* last);    //帶有兩個參數的構造 函數,使用默認的Comparison作為第三個參數
size_type;    //正整數類型,和Sequence::size_type類型一樣。
bool empty() const;    //判斷優先級隊列是否為空,為空返回true,否則返回false
size_type size() const;    //返回優先級隊列中的元素個數
const value_type& top() const();    //返回優先級隊列中第一個元素的參考值。
void push(const value_type& x);    //把元素x插入到優先級隊列的尾部,隊列的長度加1
void pop();    //刪除優先級隊列的第一個值,前提是隊列非空,刪除后隊列長度減1

關聯容器

它運用了鍵值對(value-key),與java類似的map,例如hashmap,有點在于他提供了利用key快速訪問功能,它的底層結構應該是一種樹來實現的,所以他才有如此快的查找速度,最簡單的set,他的鍵值對類型是一致的,而且唯一,元素默認按升序排列。map他的鍵值對類型不同,鍵是唯一的,元素默認按鍵的升序排列。!而muilti_sset/map 鍵可以不唯一。

迭代器在關聯容器中對操作:

m.lower_bound(k)//返回一個迭代器,指向鍵不小于 k 的第一個元素
m.upper_bound(k)//返回一個迭代器,指向鍵大于 k 的第一個元素
m.equal_range(k)//返回一個迭代器的 pair 對象。它的 first 成員等價于m.lower_bound(k) //。而 second 成員則等價于 m.upper_bound(k)

map

map 是鍵-值對的集合。map 類型通常可理解為關聯數組:可使用鍵作為下標來獲取一個值,正如內置數組類型一樣。而關聯的本質在于元素的值與某個特定的鍵相關聯,而并非通過元素在數組中的位置來獲取。

定義與初始化

map<int,string> map1;    //默認為空
m.insert()
    // 1.m.insert(e)//e是一個用在m上的value_kry 類型的值。如果鍵(e.first不在m中,則插入一個值為e.second 的新元素;如果該鍵在m中已存在,則保持m不變。該函數返回一個pair類型對象,包含指向鍵為e.first的元素的map迭代器,以及一個 bool 類型的對象,表示是否插入了該元素
    // 2.m.insert(begin,end)//begin和end是標記元素范圍的迭代器,其中的元素必須為m.value_key 類型的鍵-值對。對于該范圍內的所有元素,如果它的鍵在 m 中不存在,則將該鍵及其關聯的值插入到 m。返回 void 類型
    // 3.m.insert(iter,e)//e是一個用在m上的 value_key 類型的值。如果鍵(e.first)不在m中,則創建新元素,并以迭代器iter為起點搜索新元素存儲的位置。返回一個迭代器,指向m中具有給定鍵的元素
m.count(k) //返回m中k的出現次數
m.find()   //如果m容器中存在按k索引的元素,則返回指向該元素的迭代器。如果不存在,則返回超出末端迭代器.
m.erase()  //具體與序列該方法一致!

set

支持插入,刪除,查找等操作,就像一個集合一樣。所有的操作的都是嚴格在logn時間之內完成,效率非常高。set和multiset的區別是:set插入的元素不能相同,但是multiset可以相同。Set默認自動排序。使用方法類似list。
set容器的定義和使用

set 容器的每個鍵都只能對應一個元素。以一段范圍的元素初始化set對象,或在set對象中插入一組元素時,對于每個鍵,事實上都只添加了一個元素。

vector<int> ivec;
for(vector<int>::size_type i = 0; i != 10; ++i) {
ivec.push_back(i);
ivec.push_back(i);
}
set<int> iset(ivec.begin(), ivec.end());
cout << ivec.size() << endl;//20個
cout << iset.size() << endl;// 10個

添加

set<string> set1;
set1.insert("the"); //第一種方法:直接添加
set<int> iset2;
iset2.insert(ivec.begin(), ivec.end());//第二中方法:通過指針迭代器

獲取:

set<int> iset;
for(int i = 0; i<10; i++)
iset.insert(i);
iset.find(1)// 返回指向元素內容為1的指針
iset.find(11)// 返回指針iset.end()
iset.count(1)// 存在,返回1
iset.count(11)// 不存在,返回0

由于其他兩個不常用我們不做過多介紹!有興趣的童鞋可以去CPPAPI或者 CPP底層源碼參考學習!

無序關聯容器

  • unordered_set
  • unordered_multiset
  • unordered_map
  • unordered_multimap

底層結構基于哈希表,主要與提高添加與刪除元素得速度與提高查找算法得效率!無序關聯容器(unordered_set、unordered_multiset、unordered_map和 unordered_multimap)使用鍵和哈希表,以便能夠快速存取數據。

下面簡要地介紹這些概念。哈希函數(hash function)將鍵轉換為索引值。例如,如果鍵為string對象,哈希函數可能將其中每個字符的數字編碼相加,再計算結果除以13的余數,從而得到 一個0~12的索引。

而無序容器將使用13個桶(bucket)來存儲string,所有索引為4的string都將存儲在第4個桶中。如果您要在容器中搜索鍵,將對鍵執行哈希函數,進而只在索引對應的桶中搜索。理想情況下,應有足夠多的桶,每個桶只包含為數不多的string。

C++11庫提供了模板hash<key>,無序關聯容器默認使用該模板。為各種整型、浮點型、指針以及一些模板類(如string)定義了該模板的具體化。

X(n, hf, eq)//創建一個至少包含n個桶的空容器,并將hf用作哈希函數,將eq用作鍵值相等謂詞。如果省略了eq,則將ke

y_equal( )用作鍵值相等謂詞;如果也省略了hf,則將hasher( )用作哈希函數

X a(n, hf, eq)//創建一個名為a的空容器,它至少包含n個桶,并將hf用作哈希函數,將eq用作鍵值相等謂詞。如果省略eq,則將key_equal( )用作鍵值相等謂詞;如果也省略了hf,則將hasher( )用作哈希函數

X(i, j, n, hf, eq)//創建一個至少包含n個桶的空容器,將hf用作哈希函數,將eq用作鍵值相等謂詞,并插入區間[i, j]中的元素。如果省略了eq,將key_equal( )用作鍵值相等謂詞;如果省略了hf,將hasher( )用作哈希函數;如果省略了n,則包含桶數不確定

X a(i, j, n, hf, eq)//創建一個名為a的的空容器,它至少包含n個桶,將hf用作哈希函數,將eq用作鍵值相等謂詞,并插入區間[i, j]中的元素。如果省略了eq,將key_equal( )用作鍵值相等謂詞;如果省略了hf,
將hasher( )用作哈希函數;如果省略了n,則包含桶數不確定

b.hash_function( )//返回b使用的哈希函數

b.key_eq( )//返回創建b時使用的鍵值相等謂詞

b.bucket_count( )//返回b包含的桶數

b.max_bucket_count ( )//返回一個上限數,它指定了b最多可包含多少個桶

b.bucket(k)//返回鍵值為k的元素所屬桶的索引

b.bucket_size(n)//返回索引為n的桶可包含的元素數

b.begin(n)//返回一個迭代器,它指向索引為n的桶中的第一個元素

b.end(n)//返回一個迭代器,它指向索引為n的桶中的最后一個元素

b.cbegin(n)//返回一個常量迭代器,它指向索引為n的桶中的第一個元素

b.cend(n)//返回一個常量迭代器,它指向索引為n的桶中的最后一個元素

b.load_factor()//返回每個桶包含的平均元素數

b.max_load_factor()//返回負載系數的最大可能取值;超過這個值后,容器將增加桶

b.max_load_factor(z)//可能修改最大負載系統,建議將它設置為z

a.rehash(n)//將桶數調整為不小于n,并確保a.bucket_count( )> a.size( ) / a.max_load_factor( )

a.reserve(n)//等價于a.rehash(ceil(n/a.max_load_factor( ))),
其中ceil(x)返回不小于x的最小整數

用法基本都是一致,所以我在這里給大家一個可以尋找到一些數據結構的方法的路徑!故沒有給大家分析與解讀他們的一些存儲與方法作用圖用于理解!

小結

我們一般在用一些基本的數據結構的時候,為了方便與解題技巧我們一般會用到容器!當然具體方法太多,所以建議用什么學什么!

  1. 有序容器(除了list):存儲底層vector,只是添加了不同的接口!
  2. deque(隊列):它不像vector 把所有的對象保存在一塊連續的內存塊,而是采用多個連續的存儲塊,并且在一個映射結構中保存對這些塊及其順序的跟蹤。向deque 兩端添加或刪除元素的開銷很小,它不需要重新分配空間。
  3. list(列表):是一個線性鏈表結構,它的數據由若干個節點構成,每一個節點都包括一個信息塊(即實際存儲的數據)、一個前驅指針和一個后驅指針。它無需分配指定的內存大小且可以任意伸縮,這是因為它存儲在非連續的內存空間中,并且由指針將有序的元素鏈接起來。
  4. 后面的關聯與無序關聯都是用的一種樹狀結構!

用法與選擇:

  1. 當數組大小未知時,和需要高效的查詢功能,用vector!高效地隨機存儲。
  2. 不使用連續的內存空間,而且可以隨意地進行動態操作,有大量的插入、刪除操作,用list!
  3. 需要在兩端進行push 、pop用daque!它兼顧了數組和鏈表的優點,它是分塊的鏈表和多個數組的聯合。所以它有被list 好的查詢性能,有被vector 好的插入、刪除性能。 如果你需要隨即存取又關心兩端數據的插入和刪除,那么deque 是最佳之選。
  4. 需要查找或者打表可以選擇map與set,當然一定條件下我們可以優秀考慮用無序關聯容器!
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容

  • STL 就是所謂的標準模板庫(Standard Template Library),這可能是C++程序員的一大利器...
    wenmingxing閱讀 21,053評論 1 18
  • STL部分 1.STL為什么廣泛被使用 C++ STL 之所以得到廣泛的贊譽,也被很多人使用,不只是提供了像vec...
    杰倫哎呦哎呦閱讀 4,332評論 0 9
  • C++ 標準模板庫(STL) 作者:AceTan,轉載請標明出處! 0x00 何為STL## STL(Standa...
    AceTan閱讀 4,976評論 3 44
  • 概念 迭代器是一種設計模式。容器生成的迭代器用于遍歷容器中的每個元素, 同時避免暴露容器的內部數據結構和實現細節....
    jdzhangxin閱讀 2,489評論 0 2
  • 前言 Standard Template Library (STL)提供了四個部分,算法,容器,函數和迭代器。這里...
    wwwzy閱讀 511評論 0 1