STL各容器的底层实现及其优缺点
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),适用于有频繁的查找的情况。元素内的元素是无序的。