既然可使用std::find_if_not算法,为什么还需要std::partition_point?
std::partition_point 与 std::find_if_not 的核心区别
二者虽然在你给出的已划分序列场景下输出一致,但本质是完全不同的工具,差异主要有三点:
- 前置要求不同
std::partition_point有严格的前置条件:输入序列必须已经按照给定谓词完成划分,也就是所有满足pred的元素全部排在序列前半段,所有不满足pred的元素全部排在后半段。你测试用的样例刚好符合这个要求:前7个元素都是奇数(满足x%2为真),后面都是偶数,所以二者输出一致。
而std::find_if_not没有任何序列有序/划分的前置要求,任意乱序序列都可以正常使用,返回第一个不满足谓词的元素。
如果你的序列没有提前做划分,比如把测试向量改成{1,2,3,4},这时候调用partition_point属于未定义行为,返回结果完全不可预期,而find_if_not会正确返回指向第二个元素2的迭代器。 - 时间复杂度差异极大
std::partition_point基于二分查找实现:
对于随机访问迭代器(比如std::vector、std::array的迭代器),时间复杂度为O(log n),最多仅需要执行log2(n) + 1次谓词判断;即使是前向迭代器,谓词判断的次数依然是O(log n),仅迭代移动次数为O(n)。
而std::find_if_not是线性遍历实现,最坏情况需要遍历整个序列,执行*O(n)*次谓词判断。如果是处理长度为100万的已划分序列,partition_point只需要做20次左右的谓词判断就可以找到边界,find_if_not最坏要跑100万次,性能差了5个量级。 - 适用场景不同
std::partition_point是专门搭配划分类算法使用的工具:当你用std::partition、std::stable_partition对序列完成划分后,要快速获取划分边界的时候用它最合适。
而std::find_if_not适用于任意无序序列,查找第一个不符合谓词的元素的场景。
补充验证代码(未划分序列下的差异)
#include <vector> #include <iostream> #include <algorithm> template <typename In_It, typename FUNC> In_It partitionPoint(In_It b, In_It e, FUNC pred){ int len = e - b; while (len > 0){ int half = len >> 1; In_It middle = b + half; if( pred(*middle) ){ b = middle; ++b; len = len - half - 1; } else len = half; } return b; } int main(){ // 未划分的序列:奇数偶数交替出现 std::vector<int> v{1,2,3,4,5,6}; auto pred = [](int x){return x % 2; }; auto it_part = partitionPoint(v.begin(), v.end(), pred); auto it_find = std::find_if_not(v.begin(), v.end(), pred); std::cout << "partition_point返回结果:下标" << it_part - v.begin() << '\n'; std::cout << "find_if_not返回结果:下标" << it_find - v.begin() << ",值为" << *it_find << '\n'; }
输出样例:
partition_point返回结果:下标6(越界,结果未定义) find_if_not返回结果:下标1,值为2
内容的提问来源于stack exchange,提问作者Itachi Uchiwa
相关产品推荐
相关产品推荐

