STL各容器的底層實現及其優缺點
序列式容器
序列式容器,即以線性排列(類似普通數組的存儲方式)來存儲某一指定類型(例如 int、double 等)的數據,需要特殊說明的是,該類容器並不會自動對存儲的元素按照值的大小進行排序。C++本身提供了array序列式容器,也就是普通的數組。
1.vector
vector和array很相似,唯一的區別是,array是靜態空間,大小一旦配置就無法更改。而vector是動態空間,更加靈活,隨着元素的增加,其內部機制會自動擴容。vector所采用的數據結構為線性連續空間,為了防止頻繁的擴容,vector在配置時一般會配置比用戶需求更大的空間,這就是容量的概率。容量始終是大於等於vector實際大小的。當滿載時,會以原大小的兩倍配置一塊新空間,將原空間的內容拷貝過來,釋放原空間。這時要注意原有的迭代器都會失效。
由於vector底層線性連續空間的特點,其在尾部插入、刪除元素的效率最高,為O(1)。但其它位置的插入刪除效率為O(n)。vector是隨機存取的,所以訪問任意的效率較高。
2.list
相較於vector的連續線性空間,list每次插入或刪除一個元素,就是配置獲釋放一個元素空間。list是一個雙向列表,其底層數據結構為一個雙向環狀列表,通過在環狀列表尾部添加一個空節點,使其滿足STL前閉后開的要求。
因為雙向鏈表的特點,list插入刪除元素的效率為O(1);但訪問任意元素的速度較慢,因為其不支持隨機訪問,只能從頭尾開始遍歷。
3.deque
deque表現為一個雙向列表。其底層實現是,動態的分段連續空間,不同於vector的一整塊連續空間,deque是通過中控器將分散的內存塊連成一塊,並動態的增加減少,可以說是邏輯上的連續空間。也因為其底層實現的復雜,除非必要,應盡量使用vector。對於deque的排序,為了提高效率,可以先復制到vector中排序,然后再復制回deque。
得益於deque的底層實現,其在頭尾插入刪除元素為O(1),但在任意位置插入刪除元素的時間復雜度為O(n)。
4. Stack,queue
stack,queue都是容器配接器,其底層是基於其它容器實現。stack的特點是元素先進后出,queue的特點是先進后出。
5. priority_queue
優先隊列,是一個帶有價值觀念的queue,其內的元素並非按照被推入的順序排列,而是自動按照元素的權值排列。權值最高者,排在最前面。缺省情況下,STL中的priority_queue基於一個max_heap(大根堆),而后者是一個vector變現的完全二叉樹(complete binary tree)。
關聯式容器
關聯式容器不同於序列式容器,在存儲元素值的同時,還會為各元素額外再配備一個值(稱為“鍵”),它的功能是在使用關聯式容器的過程中,如果已知目標元素的鍵的值,則直接通過該鍵就可以找到目標元素,而無需再通過遍歷整個容器的方式。棄用序列式容器,轉而選用關聯式容器存儲元素,往往就是看中了關聯式容器可以快速查找、讀取或者刪除所存儲的元素,同時該類型容器插入元素的效率也比序列式容器高。
面對關聯式容器,應該使用容器所提供的find函數來搜尋元素,會比STL算法find()更有效率。因為STL算法find()只是循環搜尋。
關聯式容器沒有所謂頭尾,只有最大、最小元素,所以沒有,push_back(),pus_front()這類的操作。
1. map, set , multimap, multiset
之所以把這四種容器放在一起,是因為它們的底層實現都是基於紅黑樹。
map的特性是,所有元素會依據元素的鍵值自動被排序。map的所有元素都是pair,即鍵值對(value和key),map不允許擁有相同的鍵值。map不允許修改鍵值,允許修改實值。
set的特性是,所有元素會依據元素的鍵值自動被排序。set的元素,鍵值就是實值,實值就是鍵值,set不允許兩個元素有相同的元素。同時,由於set元素的特點,set的元素值是只讀的,因為set的值是其排列規則的依據,如果隨意修改,會破壞set 的組織。
multimap,multiset和map,set的唯一區別就是前者允許鍵值重復。
2. unordered_map,unordered_set, unordered_multimap, unordered_multiset
這四種容器和非unordered版本的區別在於其底層實現,這四種均是基於hash表實現,所以默認是沒有順序的。
選擇依據:
- 基於RB-tree的關聯式容器,空間占用率高,因為map內部實現了紅黑樹,雖然提高了運行效率,但是因為每一個節點都需要額外保存父節點、孩子節點和紅/黑性質,使得每一個節點都占用大量的空間。其查找時間復雜度為O(logn)。此外,容器內的元素是有序的。
- 基於hash表的關聯式容器,建表的過程時間消耗大,查找效率高,為O(1),適用於有頻繁的查找的情況。元素內的元素是無序的。