std::ranges::stable_partition - cppreference.com (original) (raw)
| Defined in header | ||
|---|---|---|
| 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) |
Reorders the elements in the range
[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.Same as (1), but uses 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 algorithm function objects (informally known as niebloids), that is:
- Explicit template argument lists cannot be specified when calling any of them.
- None of them are visible to argument-dependent lookup.
- When any of them are found by normal unqualified lookup as the name to the left of the function-call operator, argument-dependent lookup is inhibited.
Contents
[edit] Parameters
| first, last | - | the iterator-sentinel pair defining 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 |
[edit] Return value
- An object equal to {pivot, last}, where
pivotis an iterator to the first element of the second group.
[edit] Complexity
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.
[edit] 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 |
[edit] 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 S, class Proj = std::identity, std::indirect_unary_predicate<std::projected<I, Proj>> Pred> requires std::permutable constexpr ranges::subrange 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, Proj>> Pred> requires std::permutable<ranges::iterator_t> constexpr ranges::borrowed_subrange_t 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 {};
[edit] Example
#include #include #include #include namespace rng = std::ranges; template<std::permutable I, std::sentinel_for 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 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