目录
3,lower_bound、upper_bound、equal_range
C++标准程序库中,属于STL范畴的,粗略估计在80%以上。STL标准模板库是容器和算法组成的。
一,容器
1,所有容器
(1)数组 array 向量 vector
(2)栈 stack
(3)队列 queue 优先队列 priority_queue 双端队列 deque
(4)键值对集合 map multimap unordered_map unordered_multimap
(5)集合 set multiset unordered_set unordered_multiset
(6)链表 list forward_list
在SGI STL中的4个容器是hash_set、hash_map、hash_multiset、hash_multimap(和unordered_***对应)
2,常用容器
容器可以分为3类,常用容器的分类:
(1)顺序容器
链表 list 向量 vector 双端队列 deque
(2)关联容器
set multiset unordered_set
map unordered_map
(3)容器适配器
栈 stack 队列 queue 优先队列 priority_queue
3,容器主要用法
(1)顺序容器
链表 list 向量 vector 双端队列 deque
vector是连续内存,data()可以获取其中数组地址。
list是链表,不提供随机访问,支持merge。
(2)关联容器
set multiset unordered_set
map unordered_map
PS:contains是C++20的新函数
(3)容器适配器
栈 stack 队列 queue 优先队列 priority_queue
4,resize和reserve
vector<int>v(3);
cout<<v.size();
v.reserve(5);
cout<<v.size();
v.resize(5);
cout<<v.size();
输出:3 3 5
这说明,reserve是预留空间,但是并不在容器中,而是在容器外,而resize是直接往容器中扩充了空间。
之所以要预留空间,是因为vector是动态大小,如果插入次数较多,就会涉及到多次的整体搬运内存,效率变得很低。
二,关系模型
STL中的关系模型,是基于离散数学中的二元关系来定义的。
1,谁需要关系
顺序容器、链表不需要,优先队列、关联容器需要,所有算法(包括find成员函数)都需要。
2,运算符最小集
不同的关系模型有不同的运算符最小集,常用的基础运算符是<和==
3,弱序关系和严格弱序关系
4,相等关系
(1)查找算法find需要的是相等关系
(2)最小集是{==},可以推导出!=,显然推不出序关系
5,序关系和相等关系的关系
默认情况下,我们用序关系推导出的==和!=表示等序(等价)关系。
但是,我们也可以把2个关系分开定义。
最小集:{<,==}
<可以推导>,再推导出等序关系
==可以推导出!=
等序的元素可能相等也可能不相等。
三,算法
1,sort函数
只有顺序容器可以排序,关联容器和容器适配器不能排序。
常用顺序容器: 链表 list 向量 vector 双端队列 deque
链表排序是用成员sort函数,其他顺序容器是用STL中sort函数,因为链表不支持随机访问,其他顺序容器支持随机访问。
(1)STL中的sort函数
2个必选参数:begin和end,1个可选参数:比较函数
默认升序(相当于less<>()),也可以用函数指针,包括greater<>()、less<>()、自定义函数指针、自定义仿函数
(2)list的成员sort函数
1个可选参数:比较函数
默认升序(相当于less<>()),也可以用函数指针,包括greater<>()、less<>()、自定义函数指针,不能用自定义仿函数
(3)优先队列priority_queue
优先队列在定义时需要内置比较函数,和list的成员sort函数一样:
默认升序(相当于less<>()),也可以用函数指针,包括greater<>()、less<>()、自定义函数指针,不能用自定义仿函数
小结:所有排序都是默认是less函数,优先队列是以end 作为top,所以默认升序函数就是默认大顶堆。
sort源码里面有一段是这样的:(完整代码参考快速排序和sort)
while (_First < _Pfirst
&& !_DEBUG_LT_PRED(_Pred, *(_Pfirst - 1), *_Pfirst)
&& !_Pred(*_Pfirst, *(_Pfirst - 1)))
--_Pfirst;
while (_Plast < _Last
&& !_DEBUG_LT_PRED(_Pred, *_Plast, *_Pfirst)
&& !_Pred(*_Pfirst, *_Plast))
++_Plast;
即用 !pred(x,y) && !pred(y,x) 推出x==y,所以pred只能是小于,不能是小于等于。
priority_queue里面应该原理差不多。
用这个版本的sort函数做严格弱序测试:
bool cmp(int a,int b)
{
return a<=b;
}
int main()
{
int arr[5]={1,3,2,6,2};
sort2(arr,arr+5,cmp);
return 0;
}
结果:
然而,我在一次演示的时候翻车了,运行这个代码的结果是正常的:
然后我看了源码,发现内部的sort函数实现方法果然差异很大。
2,find函数
顺序容器采用STL中的find函数,而关联容器采用成员find函数。
用法差不多,返回搜索结果的迭代器,搜索不到返回end,find使用的== 可以重载,即自定义查找条件。
(1)STL中的find函数
常用顺序容器: 链表 list 向量 vector 双端队列 deque
list<int>l;
l.push_back(1);
auto it = find(l.begin(),l.end(),8);
if(it==l.end())cout<<"not";
else cout<<"yes";
虽然链表不是连续内存,但是迭代器可以++,基于此链表可以和连续内存一样进行搜索。
时间复杂度:O(n)
(2)关联容器的成员find函数
set<int>s;
s.insert(1);
auto it = s.find(1);
if(it==s.end())cout<<"not";
else cout<<"yes";
时间复杂度:O(log n)
PS:对于multi开头的关联容器,据说是返回第一个进入容器的那一个。
3,lower_bound、upper_bound、equal_range
lower_bound、upper_bound 和 find 一样,顺序容器采用STL中的函数,而关联容器采用成员函数。
在一个从小到大的数组或者向量或者堆中,
lower_bound(first,last,k)返回第一个大于等于k的元素位置
upper_bound(first,last,k)返回第一个大于k的元素位置
也就是说,从lower_bound(k)到upper_bound(k)-1这一段都是k
比如,在有序向量中查找一个数:
bool has(vector<int>v, int k)
{
return upper_bound(v.begin(), v.end(), k) != lower_bound(v.begin(), v.end(), k);
}
equal_range是这2个函数的结合,返回pair<iterator,iterator>,2个迭代器的含义同上。
4,copy函数
set<int>s;
s.insert(1);
s.insert(22);
s.insert(3);
s.insert(2);
vector<int>v(4);
copy(s.begin(),s.end(),v.begin());
fcout(v);
输出:1 2 3 22
注意,要确保copy的目的地址容量足够,容量不够的时候reserve是不行的,必须resize才行。
5,全排列
next_permutation 下一个全排列
prev_permutation 上一个全排列
返回值是bool类型,表示操作成功或者操作失败(没有上一个或下一个全排列)
char sn[40];
next_permutation(sn,sn+32);
prev_permutation(sn,sn+32);
四,迭代器
五,用法注意总结
1,map的查完存储现象
map查询之后,就会存储查询的键值对,导致map的内存越来越大。
map<int,int>m;
cout<<m.size();
cout<<m[1];
cout<<m.size();
输出:001
这也是为什么,用[]访问map是非const的,即使本意只是读。