◐ Shell
clean mode source ↗

std::partition_point - cppreference.com

来自cppreference.com

template< class ForwardIt, class UnaryPred >
ForwardIt partition_point( ForwardIt first, ForwardIt last, UnaryPred p );
(C++11 起)
(C++20 起为 constexpr)

检验(如同用 std::partition)已划分范围 [firstlast),并定位第一分段的结尾,即首个不满足 p 的元素,或者在所有元素满足 p 时是 last

如果 [firstlast) 的元素 elem 没有按表达式 bool(p(elem)) 划分,那么行为未定义。

参数

first, last - 要检查的已划分元素范围的迭代器对
p - 对于在范围起始找到的元素则返回 ​true 的一元谓词。

对每个(可为 const 的) VT 类型参数 v ,其中 VTForwardIt 的值类型,表达式 p(v) 必须可转换到 bool,无关乎值类别,而且必须不修改 v 。从而不允许 VT& 类型参数,亦不允许 VT ,除非对 VT 而言移动等价于复制(C++11 起)。 ​

类型要求
-ForwardIt 必须满足老式向前迭代器 (LegacyForwardIterator)
-UnaryPred 必须满足谓词 (Predicate)

返回值

[firstlast) 内第一分段结尾后的迭代器。

复杂度

给定 Nstd::distance(first, last),应用 O(log(N)) 次谓词 p

注解

此算法是 std::lower_bound 的更通用化的形式,它可以表达为以 [&](const auto& e) { return e < value; }); 为谓词调用 std::partition_point

可能的实现

template<class ForwardIt, class UnaryPred>
constexpr //< C++20 起
ForwardIt partition_point(ForwardIt first, ForwardIt last, UnaryPred p)
{
    for (auto length = std::distance(first, last); 0 < length; )
    {
        auto half = length / 2;
        auto middle = std::next(first, half);
        if (p(*middle))
        {
            first = std::next(middle);
            length -= (half + 1);
        }
        else
            length = half;
    }
    
    return first;
}

示例

#include <algorithm>
#include <array>
#include <iostream>
#include <iterator>

auto print_seq = [](auto rem, auto first, auto last)
{
    for (std::cout << rem; first != last; std::cout << *first++ << ' ') {}
    std::cout << '\n';
};

int main()
{
    std::array v{1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    auto is_even = [](int i) { return i % 2 == 0; };
    
    std::partition(v.begin(), v.end(), is_even);
    print_seq("划分后,v:", v.cbegin(), v.cend());
    
    const auto pp = std::partition_point(v.cbegin(), v.cend(), is_even);
    const auto i = std::distance(v.cbegin(), pp);
    std::cout << "划分点在 " << i << ";v[" << i << "] = " << *pp << '\n';
    
    print_seq("第一分段(所有偶数元素):", v.cbegin(), pp);
    print_seq("第二分段(所有奇数元素):", pp, v.cend());
}

可能的输出:

划分后,v:8 2 6 4 5 3 7 1 9
划分点在 4;v[4] = 5
第一分段(所有偶数元素):8 2 6 4
第二分段(所有奇数元素):5 3 7 1 9

参阅