2 STL 常用函数
STL 中大量的函数在熟练掌握后可以提高写代码的效率。
使用 STL 中的函数需要包含头文件<algorithm>
。
sort 函数¶
std::sort
用于排序,会根据实际情况调整底层实现算法,时间复杂度 $O(n\log n)$,再也不用手写快排/归并。
该函数可以用于数组或vector
。使用数组时,传入需要排序区间的头指针和尾指针(左闭右开);使用vector
时传入迭代器。
std::sort
可以传入第三个参数。这个参数返回bool
值,是自定义的排序模式。该函数返回true
时,第一个参数所代表的元素应排在前面。
自定义排序函数的严格弱序要求
自定义比较函数不能随意设置,它必须是严格弱序的,否则会引发未定义行为。同时满足以下三个条件的函数cmp
是严格弱序的:
- 反自反性:
cmp(a,a)
返回false
。 - 反对称性:如果
cmp(a,b)
返回true
,则cmp(b,a)
必须返回false
。 - 传递性:如果
cmp(a,b)
和cmp(b,c)
都返回true
,那么cmp(a,c)
也要返回true
。
经典的>=
和<=
都不是严格弱序的,因为不满足反自反性和反对称性。实际操作中建议使用>
或者<
。至于为什么要有严格弱序的要求,参见这篇博客。
unique 函数¶
std::unique
用于去重,说的更明白些是将多个连续的相同元素变成一个。其时间复杂度应该是 $O(n)$。
该函数的调用方法和std::sort
类似,没有第三个参数。在实际执行时,会将指定范围内的数据进行去重,并返回去重后的尾地址(去重数组返回指针,去重vector
返回迭代器)。尾地址后的元素处于未指定状态。
lower_bound 和 upper_bound 函数¶
两个函数都是在有序的数组或者vector
中寻找第一个满足某条件的元素。底层逻辑为二分查找,时间复杂度 $O(\log n)$。
std::lower_bound
:寻找第一个不小于某值的元素,并以指针或迭代器形式返回。std::upper_bound
:寻找第一个大于某值的元素,并以指针或迭代器的形式返回。
具体来说可以举一个例子。
std::lower_bound
和std::upper_bound
实际上都等价于第四个参数是函数less<type>()
。实际运用时按照这个逻辑进行推演就好。
推演的例子
已知条件是,若传入auto cmp = [](const int &a,const int &b){return a < b;};
,那么std::lower_bound
寻找的是第一个不小于某值的函数。若我传入auto cmp = [](const int &a,const int &b){return a > b;};
,那么寻找的就是第一个不大于某值的函数(前提是序列要递减)。
当然,有时候我们数组的元素和待比较的元素不是同一类型(1)。此时我们必须搞清楚这个自定义比较函数的内涵。这样不难理解,就比如lower_bound
,我们可以认为它有一个“默认的比较函数”,也就是上面的cmp
。结合lower_bound
的目标是找到第一个不小于某值的元素,可以得到:
- 比如有一个结构体数组:
结构体数组
liker
的元素是按照的likes
字段值升序排序的,想要这个liker
数组中,第一个likes
成员大于val
的结构体。显然我们可以构造一个likes
字段为val
的结构体进行比较,但这样感觉不是很优雅,所以需要深入了解自定义比较函数的内涵。
cmp
的第一个参数是数组元素,第二个参数是目标值;lower_bound
找到第一个使得cmp
返回False
的元素。
同样地,结合upper_bound
的功能,以及它的“缺省比较函数”cmp
,可以得到:
cmp
的第一个参数是目标值,第二个参数是数组元素;upper_bound
找到第一个使得cmp
返回True
的元素。