参考
http://www.cplusplus.com/reference/stl/
分类
通用接口
类型别名
iterator
const_iterator
size_type
difference_type
value_type
reference
const_reference
构造函数
赋值
assign(仅顺序容器)
1 2 3 a.assign(it1,it2); a.assign({1 ,2 ,3 ,4 }); a.assign(n,t);
assign允许从一个不同但相容的类型赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 list <string > names; vector <const char *> old; char * tmp = new char [3 ]; tmp[0 ] = '7' ; tmp[1 ] = '8' ; tmp[2 ] = '\0' ; char tmp2[4 ] = "abc" ; old.push_back(tmp); old.push_back(tmp2); names.assign(old.cbegin(), old.cend()); for (auto i : names) cout << i << " " ; cout << endl ;
array的assign不能按上述用,a.assign(1)是把assign所有位置都变成1.
swap
常数时间交换两个相同容器的内容。元素本身并未交换,只是交换了数据结构(比如迭代器的指向)
但是array特殊,相当于数组。会真正交换其元素。也不是常数时间。
对string
调用swap会导致迭代器、引用和指针失效。
1 2 3 4 5 6 7 8 9 10 string s1 ("123" ) ;string s2 ("456" ) ;string ::iterator it0 = s1.begin();swap(s1, s2); auto it1 = s1.begin();cout << *it0 << endl ;cout << *it1 << endl ;cout << s1 << endl ;cout << s2 << endl ;
1 2 3 4 5 6 7 8 9 10 11 vector <int > v1 = {1 ,2 ,3 };vector <int > v2 = {4 ,5 ,6 ,7 };auto it0 = v1.begin();swap(v1, v2); auto it1 = v1.begin();cout << *it0 << endl ;cout << *it1 << endl ;for (auto i : v1)cout << i << " " ; cout << endl ;for (auto i : v2)cout << i << " " ; cout << endl ;
大小
empty
size
不支持forward_list
max_size
capacity,reserce,shrink_to_fit
capacity,reserve只适用于vector和string
reserve永远不会减少容量,只能用shrink_to_fit减少。
shrink_to_fit将capacity减少为于size相同大小,并请求 将超出当前大小的多余内存退回给操作系统,但只是一个请求,并不能保证一定退还内存,这是操作系统的事。
1 2 3 4 5 6 7 vector <int >a;for (int i = 0 ; i < 100 ; ++i) a.push_back(i);cout << a.size() << endl ;cout << a.capacity() << endl ;a.shrink_to_fit(); cout << a.size() << endl ;cout << a.capacity() << endl ;
每次扩容当大小超过capacity时,都要重新分配大小,将原有的元素移到新位置,释放旧内存,vector的扩张操作通常比list和deque快。
扩容分配策略遵循原则:通过一个初始为空的vector上调用n次push_back()来创建n个元素所花费的时间不能超过n的常数倍。
获取迭代器
反向容器的额外成员
添加
vector,deuqe,string等插入元素会使迭代器,指针,引用失效。
在vector的尾部以外或者deque的首尾以外任何地方添加元素,都可能 引起整个对象存储空间的重新分配。重新分配内存,将元素从旧的地址空格键移动到新的空间中。
forward_list不支持push_back和empace_back,有自己专属的emplace和insert
push_back,push_front,push
传进去的是对象的一个拷贝,而不是对象本身。
emplace_back,emplace_front,emplace
emplace_back,emplace_front,emplace分别对应push_back,push_front和insert操作。
区别是当我们调用emplace成员函数时,是将参数传递给元素类型的构造函数 ,emplace成员使用这些参数在容器管理的内存空间中直接构造元素。而push_back()则会创建一个局部临时对象,并将其压入容器。
insert
1 2 3 4 5 6 7 8 vector <int > a = {1 ,2 ,3 }; auto iter = a.begin(); for (int i = 0 ; i < 10 ; ++i) { iter = a.insert(iter, i); } for (auto i : a)cout << i << " " ; cout << endl ;
访问
返回的是引用
at
只适合vector,deque,string,array
越界则抛出out_of_range
异常。
[]
1 2 3 4 5 6 7 vector <int > a = { 1 ,2 ,3 };auto atmp = a[1 ];atmp = 10 ; for (auto i : a)cout << i << " " ; cout << endl ;auto & atmp2 = a[1 ];atmp2 = 10 ; for (auto i : a)cout << i << " " ; cout << endl ;
vector
初始化
1 2 3 4 5 6 7 8 vector <int >a(10 );vector <int >b(10 ,1 );vector <int >c={1 ,2 ,3 ,4 ,5 ,6 };vector <int >d({1 ,2 ,3 });vector <int >e(c);vector <int >f(c.begin(), c.begin() + 6 );vector <int >g(&c[0 ], &c[5 ]+1 );vector <int >e(c,c+6 );
各接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 vector <int >a={1 ,2 ,3 ,4 ,5 ,6 };a.push_back(); a.pop_back(); a.emplace_back(); a.empty(); a.size(); b.max_size(); a.capacity(); a.resize(100 ); a.reserve(100 ); a.assign(); a[1 ]; a.at(1 ); a.data(); a.clear(); a.earse(); a.swap(b);;
两种访问方式:
1 2 vector ::at()vector ::operator []
在release模式下,[]越界不会报错,at()会触发异常。
删除
clear(),pop_back(),erase()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 bool cmp (int x) { if (x < 5 ) return false ; return true ; } vector <int > b; b.push_back(1 ); b.push_back(2 ); b.push_back(3 ); b.push_back(4 ); b.push_back(5 ); b.push_back(6 ); auto it = b.begin(); b.erase(it + 1 ); for (auto i : b) cout << i << " " ; cout << endl ; auto it2 = remove_if(b.begin(), b.end(), cmp); b.erase(it2,b.end()); for (auto i : b) cout << i << " " ; b.clear();
vector<bool>较为特殊:可以参考http://www.cplusplus.com/reference/vector/vector-bool/
还有bitset(位图)和valarray(支持高速运算的数组)
emplace
参考资料
C++11 起 push_back 需要分配新内存时一般都是把元素移动构造过去,而非复制构造。
在vector文件中debug push_back()和emplace_back()的源码。观察函数调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <map> struct Complicated { int year; double country; std ::string name; Complicated(int a, double b, string c) :year(a), country(b), name(c) { cout << "is constucted" << endl ; } Complicated(const Complicated& other) :year(other.year), country(other.country), name(std ::move(other.name)) { cout << "is moved" << endl ; } ~Complicated() { cout << "Deconstruction" << endl ; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 std ::map <int , Complicated> m;int anInt = 4 ;double aDouble = 5.0 ;std ::string aString = "C++" ;cout << "—insert--" << endl ;m.insert(std ::make_pair (4 , Complicated(anInt, aDouble, aString))); cout << "—emplace--" << endl ;m.emplace(4 , Complicated(anInt, aDouble, aString)); cout << "--emplace_back--" << endl ;vector <Complicated> v;v.emplace_back(anInt, aDouble, aString); cout << "--两步push_back--" << endl ;Complicated tmp (anInt, aDouble, aString) ;v.push_back(tmp); cout << "--一步push_back--" << endl ;v.push_back(Complicated(anInt, aDouble, aString));
array
定义时,必须要指定类型和大小,底层有数组实现,所以和数组很像,但可以比数组功能强,如可以直接用=
进行array之间的赋值。
deque 双端队列
double-ended queue 的缩写,又称双端队列容器。
双端队列(deque) 连续存储的指向不同元素的指针所组成的数组
分段连续,然后串接
list 双向链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <list> void TestList () { list <int > l; l.push_back(0 ); l.push_back(4 ); l.push_back(5 ); list <int > l2; l2.push_back(1 ); l2.push_back(2 ); l2.push_back(3 ); auto it = l.begin(); advance(it, 1 ); l.insert(it, l2.begin(), l2.end()); for (auto i : l)cout << i << " " ; cout << endl ; list <int > l3 = {-3 ,-2 ,-1 }; it = l.begin(); l.splice(it, l3); for (auto i:l) cout << i << " " ; cout << endl ; cout << l3.size() << endl ; }
stack栈
先进后出
queue
先进先出,底层结构:deque/list,没有迭代器
1 2 3 4 5 6 #include<queue> queue<int> q; q.push(1); q.pop(); q.front(); q.back();
forward_list
迭代器不支持(–)
不支持反向容器的额外成员
set/multiset
1 2 3 4 template < class T , // 键 key 和值 value 的类型 class Compare = less<T>, class Alloc = allocator<T> > class set ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 set <int > set1 = {4 ,3 ,2 ,1 };set1.insert(5 ); set1.emplace(6 ); for (int i : set1) cout << i << " " ; cout << endl ;auto it = set1.find(3 );it++; cout << *it << endl ; it = set1.find(0 ); cout <<std ::boolalpha<< (it==set1.end()) << endl ;it = set1.lower_bound(3 ); cout << *it << endl ; it = set1.upper_bound(3 ); cout << *it << endl ; auto it2 = set1.equal_range(3 ); cout << *it2.first <<" " <<*it2.second<< endl ;cout << set1.size() << endl ;it = set1.find(1 ); set1.erase(it); set1.erase(2 ); set1.erase(100 ); for (int i : set1) cout << i << " " ; cout << endl ;
1 2 3 4 5 6 7 8 1 2 3 4 5 6 4 true 3 4 3 4 6 3 4 5 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 multiset <int > mset1;mset1.insert(4 ); mset1.insert(3 ); mset1.insert(2 ); mset1.insert(2 ); mset1.insert(2 ); mset1.insert(5 ); mset1.emplace(6 ); cout << mset1.count(2 ) << endl ;for (int i : mset1) cout << i << " " ; cout << endl ;auto it = mset1.find(3 );it++; cout << *it << endl ; it = mset1.find(0 ); cout << std ::boolalpha << (it == mset1.end()) << endl ;it = mset1.lower_bound(2 ); cout << *it << endl ; it = mset1.upper_bound(2 ); cout << *it << endl ; auto it2 = mset1.equal_range(2 ); cout << *it2.first << " " << *it2.second << endl ;cout << mset1.size() << endl ;it= mset1.find(2 ); mset1.erase(it); for (int i : mset1) cout << i << " " ; cout << endl ;mset1.erase(2 ); mset1.erase(100 ); for (int i : mset1) cout << i << " " ; cout << endl ;
1 2 3 4 5 6 7 8 9 10 3 2 2 2 3 4 5 6 4 true 2 3 2 3 7 2 2 3 4 5 6 3 4 5 6
map/multimap
关联容器,不允许相同的key,存储的对象必须可排序。
1 2 3 4 5 template < class Key , // 指定键(key )的类型 class T , // 指定值(value )的类型 class Compare = less<Key>, class Alloc = allocator<pair <const Key,T> > > class map ;
1 2 3 4 5 6 7 map <int ,char >mp1;mp1[0 ] = 0 + '0' ; mp1[0 ] = 0 + '0' ; cout << mp1.count(0 ) << endl ;for (int i = 5 ; i >= 0 ; --i) mp1.emplace(i, i + '0' );mp1.insert(make_pair (6 , 6 + '0' )); for (auto i : mp1)cout << i.second << " " ; cout << endl ;
1 2 3 4 5 6 7 8 9 10 multimap <int , char >mp1;for (int i = 5 ; i >= 0 ; --i) mp1.emplace(i, i + '0' );mp1.insert(make_pair (6 , 6 + '0' )); mp1.insert(make_pair (0 , 0 + '0' )); mp1.insert(make_pair (0 , 7 + '0' )); mp1.insert(make_pair (0 , 8 + '0' )); cout << mp1.count(0 ) << endl ;for (auto i : mp1)cout << i.second << " " ; cout << endl ;for (auto it = mp1.begin(); it != mp1.end(); ++it)cout << it->first << " " << it->second << endl ;
1 2 3 4 5 6 7 8 9 10 11 12 4 0 0 7 8 1 2 3 4 5 6 0 0 0 0 0 7 0 8 1 1 2 2 3 3 4 4 5 5 6 6
哈希表
哈希表(散列表)详解(包含哈希表处理冲突的方法)
哈希函数:f()是一个函数,通过这个函数可以快速求出该关键字对应的的数据的哈希地址,称之为“哈希函数”
哈希冲突:对于哈希表而言,冲突只能尽可能地少,无法完全避免。
哈希地址:哈希地址只是表示在查找表中的存储位置,而不是实际的物理存储位置。
常用的哈希函数的构造方法有 6 种:
直接定址法:其哈希函数为一次函数 ,H(key)= key 或者 H(key)=a * key + b,其中 H(key)表示关键字为 key 对应的哈希地址,a 和 b 都为常数。
数字分析法 :如果关键字由多位字符或者数字组成,就可以考虑抽取其中的 2 位或者多位作为该关键字对应的哈希地址,在取法上尽量选择变化较多 的位,避免冲突发生。
平方取中法:关键字做平方操作,取中间得几位作为哈希地址。
折叠法
除留余数法:若已知整个哈希表的最大长度 m,可以取一个不大于 m 的数 p,然后对该关键字 key 做取余运算,即:H(key)= key % p,在此方法中,对于 p 的取值非常重要,由经验得知 p 可以为不大于 m 的质数或者不包含小于 20 的质因数的合数 。
随机数法:是取关键字的一个(伪)随机函数值作为它的哈希地址,即:H(key)=random(key),此方法适用于关键字长度不等的情况。
处理冲突的方法
开放定址法 : H(key)=(H(key)+ d)MOD m(其中 m 为哈希表的表长,d 为一个增量) 当得出的哈希地址产生冲突时,选取以下 3 种方法中的一种获取 d 的值,然后继续计算,直到计算出的哈希地址不在冲突为止,这 3 种方法为:
线性探测法:d=1,2,3,…,m-1
二次探测法:d=12,-12,22,-22,32,…
伪随机数探测法:d=伪随机数
再哈希法
链地址法
建立一个公共溢出区
unordered
关联式容器的底层实现采用的树存储结构,更确切的说是红黑树结构;
无序容器的底层实现采用的是哈希表的存储结构。
C++ STL 底层采用哈希表实现无序容器时,会将所有数据存储到一整块连续的内存空间中,并且当数据存储位置发生冲突时,解决方法选用的是“链地址法
”(又称“开链法”)。
无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键,
和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。
https://blog.csdn.net/wusecaiyun/article/details/46723363
1 2 3 4 5 6 7 8 9 10 11 12 map, set, multimap, and multiset 上述四种容器采用红黑树实现,红黑树是平衡二叉树的一种。不同操作的时间复杂度近似为: 插入: O(logN) 查看:O(logN) 删除:O(logN) hash_map, hash_set, hash_multimap, and hash_multiset 上述四种容器采用哈希表实现,不同操作的时间复杂度为: 插入:O(1),最坏情况O(N)。 查看:O(1),最坏情况O(N)。 删除:O(1),最坏情况O(N)。 记住,如果你采用合适的哈希函数,你可能永远不会看到最坏情况。但是记住这一点是有必要的。