关联式容器存储的是键值对,而不是单个数据了,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。
1.map set/multimap,multiset
这四个底层用的是平衡搜索树(具体是红黑树,红黑树是平衡搜索树的优化)。数据是按键大小排序过的。
他们插入,删除,查询的时间复杂度都是**O(logn),**但是按理说红黑树插入只需要三次旋转就可以,比二叉搜索树快,所以是近似log级别。
另外,set是一个集合,插入是实际上只需要插入一个值就可以,但在底层实际存放的是由<value, value>构成的键值对。它的特点就是去重,保证元素唯一性。set迭代器是中序遍历,所以返回的是有序的。multiset与set的唯一区别是允许键值冗余,可以存储相同的数据,不怎么用到。
multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。
2、 unordered_map、unordered_set、unordered_multimap、 unordered_multiset
这几个底层是哈希表,遍历的数据是无序的,插入删除查询时间复杂度都是常数,最坏是线性(哈希冲突)
构造函数。hash_map需要hash函数,等于函数;map只需要比较函数(小于函数).
迭代器及其失效的情况
STL的 算法 和 容器 是分离的,两者通过迭代器连接。算法的实现并不知道自己被传进来的参数是什么类型的容器。迭代器相当于一种泛型指针。
(失效和容器的底层内存实现有关)
序列式容器:以vector为例:如果push_back添加元素,如果容器有剩余空间,直接添加到尾部,这时只有end()失效,其他安全;如果没有空间了,会导致容器重新分配内存,然后将数据从原内存复制到新内存,再在尾部添加新元素。此时,由于内存重新分配,原迭代器(所有)都失效。 pop_back删除最后一个元素时最后一个元素的迭代器和end()会失效。
erase(iterator) 将删除点及之后的元素都向前移动一位,然后删除最后一个元素。因此,原迭代器中删除点之前的迭代器都有效,删除点之后的元素迭代器都失效。
而在关联式容器中,insert不会使迭代器失效,(同样,list等非连续存储的容器,insert都不会失效,因为都是构造一个元素加到链表或者树上,不会出现空间不够的问题。)erase(iterator)只有被删除元素的迭代器失效,其他的没事(对于list也一样),所以在for循环中可以通过先保存iter++来避免这个问题。
空间配置器:空间配置器使用了两级配置器,如果配置空间大于128字节,使用一级配置器,若小于128字节,为了尽量避免内部存片,使用二级配置器。一级空间配置器直接调用malloc/free; 二级配置器使用了内存池技术和伙伴算法。
vector底层原理:
包含三个迭代器:底层是一个动态数组,包含三个迭代器,start和finish之间是已经被使用的空间范围也就是size(),end_of_storage是整块连续空间包括备用空间的尾部也就是capacity()。所以 vector 这个类本身并不存储数据的内容,通过指针 start 指向真正的存储元素的数组的地址。
当clear()时删除的是元素,容器没有删除。扩容是1.5-2倍扩容,不同平台不一样。为什么不是固定扩几个因素呢?因为这样均摊下来push_back复杂度变高了。为什么不能3,4,5倍扩呢?因为如果以 大于2 倍的方式扩容,下一次申请的内存会大于之前分配内存的总和,导致之前分配的内存不能再被使用。所以,最好的增长因子在 (1,2)之间。
list的底层是一个双向链表,以结点为单位存放数据,结点的地址在内存中不一定连续,每次插入或删除一个元素,就配置或释放一个元素空间。list不支持随机存取,如果需要大量的插入和删除,而不关心随即存取
deque是一个双向开口的连续线性空间(双端队列),在头尾两端进行元素的插入跟删除操作都有理想的时间复杂度。
随机访问频繁,元素数量变化不大(不会频繁的插入、删除)的场景,优先选用vector;
相反的场景,使用list,即元素频繁的插入删除,但不会经常进行随机反问。
除非必要,应尽可能的选择使用vector而非deque,因为 deque的迭代器比vector的迭代器要复杂的多。
除非真的需要频繁的在容器的首尾两端进行频繁的插入和删除元素的操作。
priority_queue:优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。
如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑unordered_map 。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,unordered_map 可能会让你陷入尴尬,特别是当你的unordered_map 对象特别多时,你就更无法控制了,而且unordered_map 的构造速度较慢。