C++ STL容器 底層數據結構

一、前言

1. 總的來說,STL包括幾個部分:容器,算法(泛型算法),迭代器三個主要部分(當然還包含仿函數,適配器等其他部分)

2. 容器包含兩大類:順序容器、關聯容器

順序容器 元素是按它們在容器中的位置來順序保存和訪問的。

  • 3 種 -- 可變長動態數組 vector、雙端隊列 deque、雙向鏈表 list

關聯容器 元素是按關鍵字來保存和訪問的。

  • 4 種 -- set、multiset、map、multimap
  • 默認情況下,關聯容器中的元素是從小到大排序(或按關鍵字從小到大排序)

其他

  • 3 種容器適配器:棧 stack、隊列 queue、優先級隊列 priority_queue

二、順序容器

1、vector

其底層數據結構是數組,由於數組的特點,vector也具有以下特性:
a)、O(1)時間的快速訪問;
b)、順序存儲,所以插入到非尾結點位置所需時間複雜度爲O(n),刪除也一樣;
c)、擴容規則:

當我們新建一個vector的時候,會首先分配給他一片連續的內存空間,如std::vector<int> vec,當通過push_back向其中增加元素時,如果初始分配空間已滿,就會引起vector擴容,其擴容規則跟編譯器有關,1.5 倍或 2 倍擴容:
首先重新申請一個 1.5 / 2 倍大的內存空間;
然後將原空間的內容拷貝過來;
最後將原空間內容進行釋放,將內存交還給操作系統

d)、注意事項:
根據vector的插入和刪除特性,以及擴容規則,我們在使用vector的時候要注意,在插入位置和刪除位置之後的所有迭代器和指針引用都會失效,同理,擴容之後的所有迭代器指針和引用也都會失效。

2、list  deque

list的底層數據結構爲雙向鏈表,特點是支持快速的增刪。內存地址不連續
queue爲單向隊列,爲先入先出原則。由多個連續內存塊構成,deque是 list 和 vector 的兼容
deque爲雙向隊列,其對比queue可以實現在頭尾兩端高效的插入和刪除操作

3、下面是選擇順序容器類型的一些準則

1. 如果我們需要隨機訪問一個容器則 vector 要比 list 好得多 。
2. 如果我們已知要存儲元素的個數則 vector 又是一個比 list 好的選擇。
3. 如果我們需要的不只是在容器兩端插入和刪除元素則list顯然要比 vector 好
4. 除非我們需要在容器首部插入和刪除元素否則 vector 要比 deque 好。
5. 如果只在容易的首部和尾部插入數據元素,則選擇 deque.
6. 如果只需要在讀取輸入時在容器的中間位置插入元素,然後需要隨機訪問元素,則可考慮輸入時將元素讀入到一個 List容器,接着對此容器重新排序,使其適合順序訪問,然後將排序後的 list 容器複製到一個 vector 容器中

三、關聯容器

1、map & multimap & unordered_map & unordered_multimap

map與multimap底層數據結構

  • 有序的
  • map 一對一 key-value
  • multimap  多對多 key-value
  • 區別在於,multimap允許關鍵字重複,而map不允許重複。
  • 底層數據結構均爲紅黑樹,關於紅黑樹的理解可以參考教你透徹瞭解紅黑樹一文。
  • 根據紅黑樹的原理,map與multimap可以實現 O(lgn) 的查找,插入和刪除。
  • 紅黑樹提高了運行效率,但是因爲每一個節點都需要額外保存父節點、孩子節點和紅/黑性質,使得每一個節點都佔用大量的空間

unordered_map 與unordered_multimap底層數據結構

  • 無序的
  • 底層實現爲 hash table,因此其查找速度非常的快,理論( 理想無碰撞的情況下 ) 上達到了O(n)
  • 存儲方式 根據 散列值組織成桶 以允許通過它們的鍵值直接快速訪問單個元素(具有常數平均時間複雜度
  • 每個桶保存零個或多個元素,容器的性能依賴於哈希函數的質量和桶的數量和大小
  • 哈希表的建立比較耗費時間

【注】:mulitimap

如果multimap中有多個元素具有相同的關鍵字,則這些關鍵字在容器中會相鄰存儲

獲取多個相同關鍵字對應值的方法 如下:

multimap<int, string> mMap;
typedef multimap<int, string>::iterator int_multimap;

// equal_range(key) 返回 key 對應的所有元素的起始和結束迭代器.
pair<int_multimap, int_multimap> p = mMap.equal_range(4);

for (int_multimap k = p.first; k != p.second; k++)
{
	cout << k->second << endl;
}
//authors是一個multimap容器 
string search_item("Alain"); 
int numbers=authors.count(search_item); 
auto it=authors.find(search_item); 
while(numbers) 
{ 
    cout<<iter->second<<endl; 
    ++it; 
    numbers--; 
}

相關接口操作

1.桶接口

m.bucket_count()        // 正在使用的桶的數目
m.max_bucket_count()    // 容器能容納的最多的桶的數量
m.bucket_size(n)        // 第n個桶中有多少個元素
m.bucket(k)             // 關鍵字爲k的元素在哪個桶

 2.桶迭代

local_iterator            // 可以用來訪問桶中元素的迭代器類型
const_local_iterator      // 桶迭代器的const版本
m.begin(n)、m.end(n)      // 桶n的首元素迭代器和尾後迭代器(n是什麼類型?)
m.cbegin(n)、m.cend(n)    // 與前兩個函數類似,但返回const_local_iterator

3.哈希策略

//每個桶的平均元素數量,返回float值
m.load_factor() 
//m試圖維護的平均桶大小,返回float值,要求創建的新桶的load_factor<=max_load_factor         
m.max_load_factor() 
//重新存儲,使得bucket_count>=n,且bucket_count>size/max_load_factor         
m.rehash(n)  
//重新存儲,使得m可以保存n個元素且不必rehash 
m.reserve(n)            

2、set & multiset & unordered_set & unordered_multiset

set 系與 map 系的區別

  • map中存儲的是<key-value>,而 set 可以理解爲關鍵字即值,即只保存關鍵字的容器

set & multiset底層數據結構

  • 紅黑樹,能實現O(lgn)的查找,插入,刪除操作

unordered_set & unordered_multiset

  • 底層實現爲hash table

注:所有 有序 關聯容器 數據是按 關鍵字的 字典順序 進行存儲存放的

 

四、其他

1、 priority_queue 優先級隊列

優先級隊列相當於一個有權值的單向隊列queue,在這個隊列中,所有元素是按照優先級排列的。

priority_queue根據的處理規則來調整元素之間的位置,關於堆的原理,可以參考

根據堆的特性,優先級隊列實現了取出最大最小元素時間複雜度爲 O(1) ,對於插入和刪除,其最壞情況爲O(lgn)。

詳細用法見 https://blog.csdn.net/xiaoquantouer/article/details/52015928

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章