目录
1 顺序容器
1.1 vector
可以动态增加空间大小,并不是在原空间之后续接新空间,而是以原空间大小的两倍另外配置一块较大空间。支持随机存储,常用操作:push_back,pop_back
- vector真正删除元素:v.erase(remove(v.begin(), v.end(), x), v.end())。clear,erase函数只会清空元素,但不会回收空间,remove()只是将待删除元素之后的元素移动到vector的前端,而不是删除(remove算法是稳定的)。
- auto_ptr不能作为vector元素,因为STL的标准容器规定它所容纳的元素必须是可以拷贝构造和可被转移赋值的,但share_ptr可以
- 只能在vector的尾部进行push和pop,不能在vector的头部进行push和pop。当动态添加的数据超过vector默认分配的大小时要进行整体的重新分配、拷贝与释放
- vector存储的是对象的指针,调用clear后,并不会调用这些指针所指对象析构函数,因此要在clear之前调用delete(堆对象需要手动进行delete)
- vector存储的是对象,调用clear后,自建类型的对象(int之类的)直接删除,若是外部类型,则调用析构函数(栈对象出栈后会自动调用析构函数)
- reverse预分配内存,避免内存碎片
v.insert(vec.begin(), 58) // 增加
v.push_back(34) // 增加
if (*iter == 3) iter = vec.erase(iter); else iter++; // 删除
vector<int>(v).swap(v) // 清空容器,clear,erase函数只会清空元素,但不会回收空间
find(v.begin(), v.end(), value) // 查找
1.2 list
一个双向的环状链表。常用操作:push_back,push_front,pop_front,pop_back
- operator = 赋值重载运算符
- operator == 比较重载运算符:判断是否具有相同长度,并对每个位置元素进行判断
- list存储的对象的指针,调用clear或erase函数后,并不会调用这些指针所指对象析构函数,因此要在clear之前调用delete
- list存储的是对象,调用clear后,自建类型的对象(int之类的)直接删除,若是外部类型,则调用析构函数
- list没有使用标准的sort进行排序,而是自身带了sort函数,使用归并进行排序
1.3 deque
一种双向开口的连续线性空间。由动态的以分段连续空间组合而成。功能上合并了vector和list,常用操作:push_back,push_front,pop_front,pop_back
- 支持随机访问,即支持[ ]操作符和vector.at();插入速度快,但内存消耗大
- 内存增长稳定,但访问速度稍慢
- 没有size方法
1.4 priority_queue
- vector+heap实现优先队列
- 没有size方法
1.5 heap
- 通过函数访问:make_heap,push_heap
1.6 使用区别
-
erase作用于序列容器时,会返回下一个迭代器,但当作用于关联容器时是没有返回值的
-
如果需要高效的随机存取,而不在乎插入和删除的效率,使用vector
-
如果需要大量的插入和删除,而不关心随即存取,则应使用list
-
如果需要随即存取,而且关心两端数据的插入和删除,则应使用deque
2 关联容器
2.1 set
键值就是实值,不允许两个元素有相同的键值,底层是用红黑树实现,所有元素根据键值自动排序。
- operator == 比较重载运算符:比较底层红黑树是否相等
- set常见集合操作:std::set_intersection、std::set_union、std::set_difference(求s1-s2差集)、std::set_symmetric_difference(求(s1-s2)U(s2-s1)对称差)
- 适合全内存有序容器
2.2 map
同时拥有实值和键值,根据键值自动按key升序排序,不能用sort函数,所有元素都是pair。当使用insert函数插入两个相同Key时,第二个key无效,以第一个key值为准。hash_set,hash_map,hash_mutiset底层以hashtable实现,没有自动排序功能。
- map调用erase操作后,迭代器会失效,但不返回迭代器,可以使用map.erase(iter++)
- map.insert(make_pair(key, value)) // 插入,当使用insert函数插入两个相同Key时,第二个key无效,以第一个key值为准
mymap.insert(pair<string, int>("aa",123)); // 插入,返回pair<map<string,int>::iterator,bool>
mymap.insert(map<string, int>::value_type("add",90))
map<string, int>::iterator iter = mymap.begin();
mymap.erase(iter); // 删除,调用erase操作后,迭代器会失效,但不返回迭代器,可以使用map.erase(iter++)
map<string,int>::iterator iter = mymap.find("aa"); // 查找
swap(map<string, int> a, map<string, int> b); // 交换两个map
2.3 hash_map
hash_map是一个聚合类,它继承自_Hash类,包括一个vector,一个list和一个pair,其中vector用于保存桶,list用于进行冲突处理,pair用于保存key->value结构;hash_map不是C++标准库的一部分,hash_map定义在__gnu_cxx命名空间中。
hashtable插入过程:得到key;通过hash函数得到hash值;得到桶号(一般都为hash值对桶数求模);存放key和value在桶内。
hashtable取值过程:得到key;通过hash函数得到hash值;得到桶号(一般都为hash值对桶数求模);比较桶的内部元素是否与key相等,若都不相等,则没有找到。取出相等的记录的value。
3 常见算法
random_shuffle():用来对一个元素序列进行重新排序(随机的)
*min_element(num,num+6,cmp):返回容器中最小值和最大值
*max_element(num,num+6,cmp):返回容器中最小值和最大值
*max_element(num,num+6):返回容器中最小值和最大值
nth_element(start, start+n, end) :第n大元素处于第n位置(从0开始,其位置是下标为 n的元素),并且比这个元素小的元素都排在这个元素之前,比这个元素大的元素都排在这个元素之后,但不能保证他们是有序的
sort仿函数算法排序效率由高到低(耗时由小变大):partion,stable_partition,nth_element,partial_sort,sort,stable_sort
sort和stable_sort的区别是:带有stable的函数可保证相等元素的原本相对次序在排序后保持不变
4 空间分配器
STL中的内存分配器实际上是基于空闲列表(free list)的分配策略。主要分三个文件实现:
stl_construct.h:定义了全局函数construct()和destroy(),负责对象的构造和析构。
stl_alloc.h:stl_alloc.h中定义了两级配置器,配置器名为alloc,主要思想是申请大块内存池,小块内存直接从内存池中申请,当不够用时再申请新的内存池,还有就是大块内存直接申请。
stl_uninitialized.h:定义了一些全局函数,用来填充(fill)或复制(copy)大块内存数据
5 常见问题
5.1 vector中v[i]和v.at(i)的区别
- 如果vector非空,那么v[i]和v.at(i)没有任何区别;否则v.at(0)会抛出out_of_range异常,而v[i]的行为未定义。所以需要用检查下标,请使用at
- C++标准为了节省程序的性能开销,不对operator[]进行下标检查
5.2 list和vector区别
- vector使用连续内存存储的,支持[]运算符,而list是以链表形式实现的,不支持[]
- vector随机访问的速度快,但是对于插入尤其是在头部插入元素速度很慢,在尾部插入速度很快。list插入速度快,但随机访问速度慢
5.3 hash_map和map区别
- hash_map底层使用hashtable实现,不会自动排序;map使用红黑树,会自动排序
- hash_map需要hash函数,等于函数,hash函数比较耗时,需要更多内存来保存hash桶元素,而且hash_map的构造速度较慢;map只需要比较函数(小于函数)
- hash_map 查找速度会比map快,而且查找速度基本和数据量大小无关,属于常数级别;而map的查找速度是log(n)级别,但并不一定常数就比log(n) 小,当操作次数较少时,建议使用map, 使用hash_map可能产生O(n)的时间复杂度
5.4 hash_map和map的使用场景
- 查找速度:hash_map 查找速度会比map快,
- 数据量:hash_map的查找算法与数据量无关,
- 内存使用:hash_map更耗内存,hash函数构造慢
- 删除和插入操作较多的情况下,map比hash_map的性能更好,添加和删除的数据量越大越明显
- 保存的数据不超过100万份,查找的频繁程度不高情况下使用map性能比较好;而保存的数据较多时(超过100万),查找频繁时使用hash_map的性能就高于map了
5.4 unordered_map 与 map区别
- map底层为红黑树查找大致为logN的时间复杂度;unordered_map(C++ 11引入)底层是闭散列的哈希桶,查找为O(1),性能更优
- 调用insert操作,map相较于unordered_map操作慢,大致有2到3倍差异;但是map插入更加稳定
- unordered_map的erase操作会缩容,导致元素重新映射,降低性能
- unordered_map要求传入的数据能够进行大小比较,“==”关系比较;所以自定义数据需要定置hash_value仿函数同时重载operator==