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) |
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) |
[first, last)
in such a way that the projection proj
of all elements for which the predicate pred
returns true
precede the projection proj
of elements for which predicate pred
returns false
. The algorithms is stable, i.e. the relative order of elements is preserved.r
as the range, as if using ranges::begin(r)
as first
and ranges::end(r)
as last
.The function-like entities described on this page are niebloids, that is:
In practice, they may be implemented as function objects, or with special compiler extensions.
first, last | - | the range of elements to reorder |
r | - | the range of elements to reorder |
pred | - | predicate to apply to the projected elements |
proj | - | projection to apply to the elements. |
{pivot, last}
, where pivot
is an iterator to the first element of the second group.r
is an lvalue or of a borrowed_range
type. Otherwise returns std::ranges::dangling
.Given N = ranges::distance(first, last)
, the complexity is at worst \(\scriptsize N\cdot\log{(N)}\)N·log(N) swaps, and only \(\scriptsize \mathcal{O}(N)\)𝓞(N) swaps in case an extra memory buffer is used. Exactly \(\scriptsize N\)N applications of the predicate pred
and projection proj
.
This function attempts to allocate a temporary buffer. If the allocation fails, the less efficient algorithm is chosen.
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> 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>> ranges::borrowed_subrange_t<R> operator()( R&& r, Pred pred, Proj proj = {} ) const { return (*this)(ranges::begin(r), ranges::end(r), std::move(pred), std::move(proj)); } }; inline constexpr stable_partition_fn stable_partition{}; |
#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:\t", original.begin(), original.end(), "\n"); vi = original; const auto ret1 = rng::stable_partition(vi, even); print("Stable partitioned:\t", vi.begin(), ret1.begin(), 0); print("│ ", ret1.begin(), ret1.end()); vi = original; const auto ret2 = rng::partition(vi, even); print("Partitioned:\t\t", 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
(C++20) | divides a range of elements into two groups (niebloid) |
(C++20) | copies a range dividing the elements into two groups (niebloid) |
(C++20) | determines if the range is partitioned by the given predicate (niebloid) |
divides elements into two groups while preserving their relative order (function template) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/ranges/stable_partition