STL容器之数据结构图解
STL容器由于各自用途的不同,底層實現的數據結構也有所不同。
具體來講,容器的主要用途就是對其中存儲的數據進行“增刪改查”。那么不同的數據結構的設計,增刪改查的效率是不一樣的。
下面是《The C++ Standard Library 2nd》的一個截圖,
我們可以看到,STL容器的類型可以分為兩種:序列容器和關聯容器。
關聯容器中又包括不定序容器。不定序容器是指元素在容器中的位置是不確定的,也就是減少了關聯容器的排序的過程,對于不需要排序的數據來講,效率會更高一些。
下面分別討論下。
std::array
從圖中可以看出來,stl中的array是固定大小容器,底層實現是C-Style array。
這個容器和vector和C類型數組的功能類似,那么它的優勢在哪?
1、和std::vector相比,std::array是靜態數組實現,在編譯時就可以確定大小,所以沒有vector的容量動態擴充時的消耗。當然也就沒有vector靈活。
2、和C-Style array相比,
std::array保持了和一般STL容器的一致的接口函數,迭代器,這樣可以更好的利用STL的算法,在使用上也更為靈活。
std::array不會隱式轉成指針,在作為參數傳遞時,也不會丟失大小信息。
std::vector
底層也是數組,和array不同的是可以動態改變大小。
內存連續。所以可以通過迭代器,指針和偏移量來隨機訪問。
容量動態擴充。當插入新的元素時,如果空間不足,會重新申請一塊2倍于當前容量的內存,并和當前內存空間做交換。原來的指針和引用會失效。
std::deque
雙端隊列。從上面這個圖可以看出來,deque可以實現兩端的插入和刪除,同時又支持隨機訪問。但其實底層實現比上面這張圖實現的更復雜。
以下這個圖很好的闡明了底層的實現,轉自知乎
1. deque是由多塊不連續的內存chunk組成的,再用一個表來存儲索引每一個chunk,數據存儲在chunk中,每個chunk的大小是相同的。
這樣設計最重要的原因是:
同時保證在兩端的插入刪除和隨機訪問可以在常量時間內完成。
這其實兼容了vector的隨機訪問和后面要提到的list常量時間插入刪除的優點。
2. deque可以做到動態大小。但是又不像vector需要重新申請內存和內存交換。
3. deque的隨機訪問需要做兩次定位。先查詢索引表,再查詢chunk中的偏移。
std::queue和std::stack是對deque的封裝。
std::list,std::forward_list
前者雙向鏈表,后者單向鏈表。
由于是鏈表,所以兩個容器都可以實現O(1)的任意位置的插入和移除。但都不支持隨機訪問。
前者提供雙向迭代器,同時在空間上占用的更多。
后者只提供在頭部插入和刪除,空間占用比list更高效,如果只需要在一端操作,forward_list更適合。
std::set, std::map, std::multiset, std::multimap
關聯型容器底層實現都是紅黑樹。
為什么要使用紅黑樹呢?因為關聯型容器對查詢的效率要求很高。而紅黑樹是一種自平衡二叉搜索樹(屬于二叉搜索樹 BST)。
我們知道在BST中,左子樹小于父節點,右子樹大于父節點。所以查詢就是遍歷節點的過程。如果樹的結構是平衡二叉樹,也就是節點的高度之間不會大于2,那么查詢的復雜度就是O(logn),這還不僅是平均時間復雜度,在最壞的情況下也是如此,因為平衡。
紅黑樹通過一些規則限制,保證節點在創建,插入和刪除后,依然能保持平衡。
所以這些關聯型容器的插入,查詢,刪除的時間復雜度都是O(logn)
后面我要總結一篇紅黑樹的文章。
std::unordered_set,std::unordered_map
這兩個也是關聯型容器,不同的是內部存儲的數據并不按照一定的順序排列。
為了實現平均時間復雜度O(1)的搜索,插入和刪除,底層的實現采用哈希表的方式。
鏈地址法:HashTable由多個bucket組成,bucket以hashkey為索引,bucket里面存儲的是相同hashkey的元素。
總結
以上是生活随笔為你收集整理的STL容器之数据结构图解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: attachment绑相对url
- 下一篇: 荣耀20自带钢化膜吗(荣耀官网荣耀手机)