std::ranges::stable_partition - cppreference.com
Namespaces
Variants

std::ranges::stable_partition

From cppreference.com
 
 
Algorithm library
Constrained algorithms and algorithms on ranges (C++20)
Constrained algorithms, e.g. ranges::copy, ranges::sort, ...
Non-modifying sequence operations    
Batch operations
(C++17)
Search operations
Modifying sequence operations
Copy operations
(C++11)
(C++11)
Swap operations
Transformation operations
Generation operations
Removing operations
Order-changing operations
(until C++17)(C++11)
(C++20)(C++20)
Sampling operations
(C++17)

Sorting and related operations
Partitioning operations
(C++11)    

Sorting operations
Binary search operations
(on partitioned ranges)
Set operations (on sorted ranges)
Merge operations (on sorted ranges)
Heap operations
Minimum/maximum operations
(C++11)
(C++17)
Lexicographical comparison operations
Permutation operations


 
Constrained algorithms
All names in this menu belong to namespace std::ranges
Non-modifying sequence operations
Fold operations (Helper templates)
Modifying sequence operations
Partitioning operations
Sorting operations
Binary search operations (on sorted ranges)
       
       
Set operations (on sorted ranges)
Heap operations
Minimum/maximum operations
       
       
Permutation operations
Specialized <memory> algorithms
Return types
 
Defined in header <algorithm>
Call signature
template< std::bidirectional_iterator I, std::sentinel_for<I> S,
          class Proj = std::identity,
          std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
    requires std::permutable<I>
ranges::subrange<I>
    stable_partition( I first, S last, Pred pred, Proj proj = {} );
(1) (since C++20)
(constexpr since C++26)
template< ranges::bidirectional_range R, class Proj = std::identity,
          std::indirect_unary_predicate
              <std::projected<ranges::iterator_t<R>, Proj>> Pred >
    requires std::permutable<ranges::iterator_t<R>>
ranges::borrowed_subrange_t<R>
    stable_partition( R&& r, Pred pred, Proj proj = {} );
(2) (since C++20)
(constexpr since C++26)
template< /*execution-policy*/ Ep,
          std::random_access_iterator I, std::sized_sentinel_for<I> S,
          class Proj = std::identity,
          std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
    requires std::permutable<I>
ranges::subrange<I>
    stable_partition( Ep&& policy, I first, S last,
                      Pred pred, Proj proj = {} );
(3) (since C++26)
template< /*execution-policy*/ Ep,
          /*sized-random-access-range*/ R, class Proj = std::identity,
          std::indirect_unary_predicate
              <std::projected<ranges::iterator_t<R>, Proj>> Pred >
    requires std::permutable<ranges::iterator_t<R>>
ranges::borrowed_subrange_t<R>
    stable_partition( Ep&& policy, R&& r, Pred pred, Proj proj = {} );
(4) (since C++26)

For the definition of /*execution-policy*/, see this page; for the definition of /*sized-random-access-range*/, see this page.

1,2) Partitions the elements e in the target range [firstlast) or r with respect to the expression bool(std::invoke(pred, std::invoke(proj, e))): all elements (projected by proj) satisfy pred appear before all elements that do not. The relative order of the elements in both groups is preserved.
3,4) Same as (1,2), but executed according to policy.

The function-like entities described on this page are algorithm function objects (informally known as niebloids), that is:

Parameters

first, last - the iterator-sentinel pair defining the target range
r - the target range
pred - the predicate to be applied to the (projected) elements
proj - the projection to be applied to the elements
policy - the execution policy to use

Return value

A subrange from the partition point to the end of the target range. All elements outside the subrange satisfy p, while all elements in the subrange do not.

Complexity

Given N as ranges::distance(first, last) or ranges::distance(r):

1,2) At most N⋅log2(N) swaps (or only 𝓞(N) swaps if there is enough extra memory), and exactly N applications of pred and proj.
3,4) 𝓞(N·log(N)) swaps, and 𝓞(N) applications of pred and proj.

Exceptions

3,4) During the execution process:
  • If the temporary memory resources required for parallelization are not available, std::bad_alloc is thrown.
  • If an uncaught exception is thrown while accessing objects via an algorithm argument, the behavior is determined by the execution policy (for standard policies, std::terminate is invoked).

Notes

This function attempts to allocate a temporary buffer. If the allocation fails, the less efficient algorithm is chosen.

Feature-test macro Value Std Feature
__cpp_lib_constexpr_algorithms 202306L (C++26) constexpr stable sorting

Possible implementation

This implementation does not use extra memory buffer and as such can be less efficient. See also the implementation in MSVC STL and libstdc++.

struct stable_partition_fn
{
    template<std::bidirectional_iterator I, std::sentinel_for<I> S,
             class Proj = std::identity,
             std::indirect_unary_predicate<std::projected<I, Proj>> Pred>
        requires std::permutable<I>
    constexpr ranges::subrange<I>
        operator()(I first, S last, Pred pred, Proj proj = {}) const
    {
        first = ranges::find_if_not(first, last, pred, proj);
        I mid = first;
        while (mid != last)
        {
            mid = ranges::find_if(mid, last, pred, proj);
            if (mid == last)
                break;
            I last2 = ranges::find_if_not(mid, last, pred, proj);
            ranges::rotate(first, mid, last2);
            first = ranges::next(first, ranges::distance(mid, last2));
            mid = last2;
        }
        return {std::move(first), std::move(mid)};
    }
    
    template<ranges::bidirectional_range R, class Proj = std::identity,
             std::indirect_unary_predicate
                 <std::projected<ranges::iterator_t<R>, Proj>> Pred>
        requires std::permutable<ranges::iterator_t<R>>
    constexpr ranges::borrowed_subrange_t<R>
        operator()(R&& r, Pred pred, Proj proj = {}) const
    {
        return (*this)(ranges::begin(r),
                       ranges::next(ranges::begin(r), ranges::end(r)),
                       std::move(pred), std::move(proj));
    }
};

inline constexpr stable_partition_fn stable_partition{};

Example

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

namespace rng = std::ranges;

template<std::permutable I, std::sentinel_for<I> S>
constexpr void stable_sort(I first, S last)
{
    if (first == last)
        return;
    
    auto pivot = *rng::next(first, rng::distance(first, last) / 2, last);
    auto left = [pivot](const auto& em) { return em < pivot; };
    auto tail1 = rng::stable_partition(first, last, left);
    auto right = [pivot](const auto& em) { return !(pivot < em); };
    auto tail2 = rng::stable_partition(tail1, right);
    
    stable_sort(first, tail1.begin());
    stable_sort(tail2.begin(), tail2.end());
}

void print(const auto rem, auto first, auto last, bool end = true)
{
    std::cout << rem;
    for (; first != last; ++first)
        std::cout << *first << ' ';
    std::cout << (end ? "\n" : "");
}

int main()
{
    const auto original = {9, 6, 5, 2, 3, 1, 7, 8};
    
    std::vector<int> vi{};
    auto even = [](int x) { return 0 == (x % 2); };
    
    print("Original vector: ", original.begin(), original.end(), "\n");
    
    vi = original;
    const auto ret1 = rng::stable_partition(vi, even);
    print("Stable partitioned: ", vi.begin(), ret1.begin(), 0);
    print("│ ", ret1.begin(), ret1.end());
    
    vi = original;
    const auto ret2 = rng::partition(vi, even);
    print("Partitioned: ", vi.begin(), ret2.begin(), 0);
    print("│ ", ret2.begin(), ret2.end());
    
    vi = {16, 30, 44, 30, 15, 24, 10, 18, 12, 35};
    print("Unsorted vector: ", vi.begin(), vi.end());
    
    stable_sort(rng::begin(vi), rng::end(vi));
    print("Sorted vector: ", vi.begin(), vi.end());
}

Possible output:

Original vector: 9 6 5 2 3 1 7 8
Stable partitioned: 6 2 8 │ 9 5 3 1 7
Partitioned: 8 6 2 │ 5 3 1 7 9
Unsorted vector: 16 30 44 30 15 24 10 18 12 35
Sorted vector: 10 12 15 16 18 24 30 30 35 44

See also