C++常見容器概述

以前的11個容器分別是

deque,list,queue,priority_queue,stack,vector,map,multimap,set,multiset,bitset

C++11新增:

array,forward_list,unordered_map,unordered_multimap,unordered_set,unordered_multiset

這裏只挑常用的講解。部分容器只作列表,沒作講述,持續更新。


以下內容只做大概的講述,詳情用法參考英文文檔      http://www.cplusplus.com/reference/s     或者中文文檔      http://classfoo.com/ccby/article/oC7Qu#sec_HL85sR


1.deque

       deque雙向隊列是一種雙向開口的連續線性空間(雖說是連續性存儲空間,但這種連續性只是表面上的,實際上它的內存是動態分配的,它在堆上分配了一塊一塊的動態儲存區,每一塊動態存儲去本身是連續的,deque自身的機制把這一塊一塊的存儲區虛擬地連在一起。),可以高效的在頭尾兩端插入和刪除元素,deque在接口上和vector非常相似。

      deque的實現比較複雜,內部會維護一個map(注意!不是STL中的map容器)即一小塊連續的空間,該空間中每個元素都是指針,指向另一段(較大的)區域,這個區域稱爲緩衝區,緩衝區用來保存deque中的數據。因此deque在隨機訪問和遍歷數據會比vector慢。

       

    如果只是平時使用deque的話,可能就想不到其內部實現細節了。下面的圖片展示deque的內部結構設計:
deque內部結構
       可以看到,deque擁有一個bitmap結構(稱之爲map),map中每一個元素指向一塊連續的內存塊,後者纔是真正存儲deque元素的地方,因爲每個塊都是固定大小的,但是每個塊之間不要求是連續的,所以擴充空間的時候,就沒有vector那樣的副作用了。

       鑑於deque的特殊設計,其迭代器也需要特殊設計,deque的iterator的設計結構如圖:
deque迭代器設計
       迭代器內部維護了四個指針,分別爲cur, first, last, node,具體在後面進行介紹。

       deque的迭代器的設計,主要是讓其看起來像一個random access iterator,因此源碼主要各種operator的重載。內部結構分爲四個指針,分別爲cur, first, last, node。

       在某一個時刻,迭代器肯定指向某個具體元素,而這個元素位於N段連續內存塊中的某一塊,其中node指向map結構中的一個節點(這個節點指向當前的內存塊),first指向當前內存塊的起始位置,cur指向當前內存塊中的特定元素節點,last指向當前內存塊的末尾位置,一圖抵千言,還是看圖來的明白:
STL deque iterator結構

      deque的保存形式如下:
[堆1]
...
[堆2]
...
[堆3]
       每個堆保存好幾個元素,然後堆和堆之間有指針指向,看起來像是list和vector的結合品,不過確實也是如此deque可以讓你在前面快速地添加刪除元素,或是在後面快速地添加刪除元素,然後還可以有比較高的隨機訪問速度.vector是可以快速地在最後添加刪除元素,並可以快速地訪問任意元素list是可以快速地在所有地方添加刪除元素,但是隻能快速地訪問最開始與最後的元素

       另外要注意一點。對於deque和vector來說,儘量少用erase(pos)和erase(beg,end)。因爲這在中間刪除數據後會導致後面的數據向前移動,從而使效率低下。

       它首次插入一個元素,默認會動態分配512字節空間,當這512字節空間用完後,它會再動態分配自己另外的512字節空間,然後虛擬地連在一起。deque的這種設計使得它具有比vector複雜得多的架構、算法和迭代器設計。它的性能損失比之vector,是幾個數量級的差別。所以說,deque要慎用.  

      deque在開始和最後添加元素都一樣快,並提供了隨機訪問方法,像vector一樣使用[]訪問任意元素,但是隨機訪問速度比不上vector快,因爲它要內部處理堆跳轉。deque也有保留空間.另外,由於deque不要求連續空間,所以可以保存的元素比vector更大,這點也要注意一下.還有就是在前面和後面添加元素時都

不需要移動其它塊的元素,所以性能也很高。


以下情形,最好採用deque:

1)需要在兩端插入和刪除元素。

2)無需引用容器內的元素。

3)要求容器釋放不再使用的元素。


2.list

       list是雙向循環鏈表,,每一個元素都知道前面一個元素和後面一個元素。在STL中,list和vector一樣,是兩個常被使用的容器。和vector不一樣的是,list不支持對元素的任意存取。list中提供的成員函數與vector類似,不過list提供對錶首元素的操作push_front、pop_front,這是vector不具備的。和vector另一點不同的是,list的迭代器不會存在失效的情況,他不像vector會保留備份空間,在超過容量額度時重新全部分配內存,導致迭代器失效;list沒有備份空間的概念,出入一個元素就申請一個元素的空間,所以它的迭代器不會失效。

       

3.queue

        隊列是一種特殊的線性表,它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。進行插入操作的端稱爲隊尾,進行刪除操作的端稱爲隊頭。隊列中沒有元素時,稱爲空隊列。在隊列這種數據結構中,最先插入在元素將是最先被刪除;反之最後插入的元素將最後被刪除,因此隊列又稱爲“先進先出”(FIFO—first in first out)的線性表。


4.priority_queue



5.stack

c++stack(堆棧)是一個容器的改編,它實現了一個先進後出的數據結構(FILO)


6.vector

      vector是STL中最常見的容器,它是一種順序容器,支持隨機訪問。vector是一塊連續分配的內存,從數據安排的角度來講,和數組極其相似,不同的地方就是:數組是靜態分配空間,一旦分配了空間的大小,就不可再改變了;而vector是動態分配空間,隨着元素的不斷插入,它會按照自身的一套機制不斷擴充自身的容量。
      vector的擴充機制:按照容器現在容量的一倍進行增長。vector容器分配的是一塊連續的內存空間,每次容器的增長,並不是在原有連續的內存空間後再進行簡單的疊加,而是重新申請一塊更大的新內存,並把現有容器中的元素逐個複製過去,然後銷燬舊的內存。這時原有指向舊內存空間的迭代器已經失效,所以當操作容器時,迭代器要及時更新。

詳情見  http://blog.csdn.net/saya_/article/details/49178021


7.map

      C++中map容器提供一個鍵值對容器,map與multimap差別僅僅在於multiple允許一個鍵對應多個值。map是一類關聯式容器,它是模板類。關聯的本質在於元素的值與某個特定的鍵相關聯,而並非通過元素在數組中的位置類獲取。它的特點是增加和刪除節點對迭代器的影響很小,除了操作節點,對其他的節點都沒有什麼影響。對於迭代器來說,不可以修改鍵值,只能修改其對應的實值。

     map的功能:


1.自動建立Key - value的對應。key 和 value可以是任意你需要的類型,但是需要注意的是對於key的類型,唯一的約束就是必須支持<操作符。
2.根據key值快速查找記錄,查找的複雜度基本是Log(N),如果有1000個記錄,最多查找10次,1,000,000個記錄,最多查找20次。
3.快速插入Key - Value 記錄。
4.快速刪除記錄
5.根據Key 修改value記錄。
6.遍歷所有記錄。



8.multimap

   

9.set

        關於set,必須說明的是set關聯式容器。set作爲一個容器也是用來存儲同一數據類型的數據類型,並且能從一個數據集合中取出數據,在set中每個元素的值都唯一,而且系統能根據元素的值自動進行排序。應該注意的是set中數元素的值不能直接被改變。C++ STL中標準關聯容器set, multiset, map, multimap內部採用的就是一種非常高效的平衡檢索二叉樹:紅黑樹,也成爲RB樹(Red-Black Tree)。RB樹的統計性能要好於一般平衡二叉樹,所以被STL選擇作爲了關聯容器的內部結構。

看下面的代碼

#include<iostream>
#include<set>

using namespace std;


int main()
{

	set<int> s;

	s.insert(2);
	s.insert(1);

	auto it=s.begin();
	*it = 2;//error,不能修改值

	for (auto x : s)
		cout << x << endl;
	return 0;
}

       在set中查找是使用二分查找,也就是說,如果有16個元素,最多需要比較4次就能找到結果,有32個元素,最多比較5次。那麼有10000個呢?最多比較的次數爲log10000,最多爲14次,如果是20000個元素呢?最多不過15次。看見了吧,當數據量增大一倍的時候,搜索次數只不過多了1次,多了1/14的搜索時間而已。你明白這個道理後,就可以安心往裏面放入元素了。


10.multiset




11.bitset

        有些程序要處理二進制位的有序集,每個位可能包含的是0(關)或1(開)的值。位是用來保存一組項或條件的yes/no信息(有時也稱標誌)的簡潔方法。標準庫提供了bitset類使得處理位集合更容易一些。注意下面的看不懂可以去我開頭推薦的網址,查看使用例子。



下面轉載一篇博客,原址是      http://blog.chinaunix.net/uid-24790746-id-252685.html


1.定義一個unsigned long型的副本
例:
unsigned long tmp = 1111;
bitset<32> bitvec(tmp);

那麼把bitvec對象給cout出來是:00000000000000000000000000001111嗎?

答案顯示是不對的,前幾天我馬虎了,原來bitset類的構造函數把tmp的十進制值給轉換到了二進制(10001010111),所以
bitvec輸出是:00000000000000000000010001010111。
如果想讓tmp直接輸出00000000000000000000000000001111的話,那麼只需將tmp更改爲十六進制0xf即可(unsgined long tmp = 0xf)。


2.定義一個string型的副本
例:
string str("1111");
bitset<32> bitvec(str);
那麼bitvec對象cout出來就是:00000000000000000000000000001111。
好了,我前幾天遇到的問題出來了,現在拿string型的副本來討論,現在bitset裏面到底是怎麼存儲str對象的字符串字面值呢??
bitvec對象裏面存儲的是00000000000000000000000000001111,
還是11110000000000000000000000000000呢?
還沒遇到這個模糊問題的時候,我用cout輸出bitvec對象的值,跟我用for循環從bitvec對象的第0位輸出到第31位的結果竟然是不一樣的。。
當執行:cout << bitvec; //result = 00000000000000000000000000001111;
當執行:
for (unsigned int ix = 0; ix != 32; ++ix)
{
 cout << bitvec[ix];
}
//result = 11110000000000000000000000000000
現在已經很清楚的知道,bitvec對象是把string對象裏面的值先轉換成二進制值,然後再倒過來存儲。
用1111可能看得不是很清楚,
那麼現在:
string str("1010");
bitset<32> bitvec(str);
通過上面那個for循環輸出的結果是:01010000000000000000000000000000,
這樣看起來就比較直觀了。一般平時要輸出bitset對象時,都是用cout輸出,因爲cout已經重載了各種各樣的運算符,用for循環輸出只是爲了更加深入的去理解bitset罷了。

#include <iostream>
#include <bitset>
/*順便提一下,這裏爲什麼不直接使用"using namespace std;"比較簡便了,因爲這樣對於比較大的程序來
說不是很好的習慣,因爲使用這句話雖然簡便了,但是這樣導致把std命名空間的所有標準庫函數都引進到程序文件裏,這樣如
果以後程序涉及到同名函數名或變量名,將會出現難以調試的錯誤*/
using std::cout;
using std::endl;
using std::bitset;
int main()
{
	bitset<32> bitvec("1010");
	//1.
	cout << bitvec;
	cout << endl;
	//2.
	for (int ix = 0; ix != bitvec.size(); ++ix)
	{
		cout << bitvec[ix];
	}
	cout << endl;
	//3.
	for (int ix = bitvec.size() - 1; ix != -1; --ix)
	{
		cout << bitvec[ix];
	}
	cout << endl;
	/*

	00000000000000000000000000001010
	01010000000000000000000000000000
	00000000000000000000000000001010

	*/
	return 0;
}


12.forward_list



13.unordered_map


14.unordered_multimap


15.unordered_set


16.unordered_multiset

發佈了57 篇原創文章 · 獲贊 5 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章