Spec-Zone.ru › C++

std::ranges::inplace_merge

Определён в заголовке <algorithm>
Сигнатура вызова
template< std::bidirectional_iterator I, std::sentinel_for<I> S,
          class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<I, Comp, Proj>
    I inplace_merge( I first, I middle, S last,
                     Comp comp = {}, Proj proj = {} );
(1) (начиная с C++20)
(constexpr начиная с C++26)
template< ranges::bidirectional_range R, class Comp = ranges::less,
          class Proj = std::identity >
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
ranges::borrowed_iterator_t<R>
    inplace_merge( R&& r, ranges::iterator_t<R> middle,
                   Comp comp = {}, Proj proj = {} );
(2) (начиная с C++20)
(constexpr начиная с C++26)

Объединяет два смежных отсортированных диапазона [first, middle) и [middle, last) в один отсортированный диапазон [first, last).

Последовательность считается отсортированной относительно компаратора comp и проекции proj, если для любого итератора it, указывающего на последовательность, и любого неотрицательного целого числа n, такого что it + n является допустимым итератором, указывающим на элемент последовательности, std::invoke(comp, std::invoke(proj, *(it + n)), std::invoke(proj, *it))) вычисляется в значение false.

Эта функция слияния является стабильной: для эквивалентных элементов в исходных двух диапазонах элементы из первого диапазона (с сохранением исходного порядка) располагаются перед элементами из второго диапазона (также с сохранением исходного порядка).

1) Элементы сравниваются с помощью заданной бинарной функции сравнения comp и объекта проекции proj; диапазоны должны быть отсортированы относительно них.
2) То же, что и (1), но в качестве диапазона используется r, как если бы в качестве first использовался ranges::begin(r), а в качестве last — ranges::end(r).

Описанные на этой странице функциональные объекты-подобные сущности являются объектами-функциями алгоритмов (неофициально известными как niebloids), то есть:

  • При вызове любой из них нельзя указать явный список аргументов шаблона.
  • Ни одна из них не видна при поиске, зависящем от аргументов.
  • Если какая-либо из них обнаружена обычным неквалифицированным поиском как имя слева от оператора вызова функции, поиск, зависящий от аргументов, подавляется.

Параметры

first - начало первого отсортированного диапазона
middle - конец первого диапазона и начало второго диапазона
last - конец второго отсортированного диапазона
r - диапазон элементов для слияния на месте
comp - функция сравнения для применения к спроецированным элементам
proj - проекция для применения к элементам диапазона

Возвращаемое значение

Итератор, равный last.

Сложность

Ровно N − 1 сравнений, если доступен дополнительный буфер памяти, где N = ranges::distance(first, last). В противном случае — \(\scriptsize \mathcal{O}(N\cdot\log{(N)})\)𝓞(N•log(N)) сравнений. Кроме того, в обоих случаях количество применений проекции вдвое превышает количество сравнений.

Примечания

Эта функция пытается выделить временный буфер. Если выделение завершается неудачей, выбирается менее эффективный алгоритм.

Макрос проверки поддержки возможностей Значение Стандарт Возможность
__cpp_lib_constexpr_algorithms 202306L (C++26) constexpr стабильная сортировка

Возможная реализация

Эта реализация показывает только менее эффективный алгоритм, используемый при отсутствии дополнительной памяти. См. также реализации в MSVC STL и libstdc++.

struct inplace_merge_fn
{
    template<std::bidirectional_iterator I, std::sentinel_for<I> S,
             class Comp = ranges::less, class Proj = std::identity>
    requires std::sortable<I, Comp, Proj>
    constexpr I operator()(I first, I middle, S last, Comp comp = {}, Proj proj = {}) const
    {
        I last_it = ranges::next(middle, last);
        inplace_merge_slow(first, middle, last_it,
                           ranges::distance(first, middle),
                           ranges::distance(middle, last_it),
                           std::ref(comp), std::ref(proj));
        return last_it;
    }

    template<ranges::bidirectional_range R, class Comp = ranges::less,
             class Proj = std::identity>
    requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
    constexpr ranges::borrowed_iterator_t<R>
        operator()(R&& r, ranges::iterator_t<R> middle,
                   Comp comp = {}, Proj proj = {}) const
    {
        return (*this)(ranges::begin(r), std::move(middle), ranges::end(r),
                       std::move(comp), std::move(proj));
    }

private:
    template<class I, class Comp, class Proj>
    static constexpr void inplace_merge_slow(I first, I middle, I last,
                                             std::iter_difference_t<I> n1,
                                             std::iter_difference_t<I> n2,
                                             Comp comp, Proj proj)
    {
        if (n1 == 0 || n2 == 0)
            return;
        if (n1 + n2 == 2 && comp(proj(*middle), proj(*first)))
        {
            ranges::iter_swap(first, middle);
            return;
        }

        I cut1 = first, cut2 = middle;
        std::iter_difference_t<I> d1{}, d2{};

        if (n1 > n2)
        {
            d1 = n1 / 2;
            ranges::advance(cut1, d1);
            cut2 = ranges::lower_bound(middle, last, *cut1,
                                       std::ref(comp), std::ref(proj));
            d2 = ranges::distance(middle, cut2);
        }
        else
        {
            d2 = n2 / 2;
            ranges::advance(cut2, d2);
            cut1 = ranges::upper_bound(first, middle, *cut2,
                                       std::ref(comp), std::ref(proj));
            d1 = ranges::distance(first, cut1);
        }

        I new_middle = ranges::rotate(cut1, middle, cut2);
        inplace_merge_slow(first, cut1, new_middle, d1, d2,
                           std::ref(comp), std::ref(proj));
        inplace_merge_slow(new_middle, cut2, last, n1 - d1, n2 - d2,
                           std::ref(comp), std::ref(proj));
    }
};

inline constexpr inplace_merge_fn inplace_merge {};

Пример

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

void print(auto const& v, auto const& rem, int middle = -1)
{
    for (int i{}; auto n : v)
        std::cout << (i++ == middle ? "│ " : "") << n << ' ';
    std::cout << rem << '\n';
}

template<std::random_access_iterator I, std::sentinel_for<I> S>
requires std::sortable<I>
void merge_sort(I first, S last)
{
    if (last - first > 1)
    {
        I middle{first + (last - first) / 2};
        merge_sort(first, middle);
        merge_sort(middle, last);
        std::ranges::inplace_merge(first, middle, last);
    }
}

int main()
{
    // custom merge-sort demo
    std::vector v{8, 2, 0, 4, 9, 8, 1, 7, 3};
    print(v, ": before sort");
    merge_sort(v.begin(), v.end());
    print(v, ": after sort\n");

    // merging with comparison function object and projection
    using CI = std::complex<int>;
    std::vector<CI> r{{0,1}, {0,2}, {0,3}, {1,1}, {1,2}};
    const auto middle{std::ranges::next(r.begin(), 3)};
    auto comp{std::ranges::less{}};
    auto proj{[](CI z) { return z.imag(); }};

    print(r, ": before merge", middle - r.begin());
    std::ranges::inplace_merge(r, middle, comp, proj);
    print(r, ": after merge");
}

Вывод:

8 2 0 4 9 8 1 7 3 : before sort
0 1 2 3 4 7 8 8 9 : after sort

(0,1) (0,2) (0,3) │ (1,1) (1,2) : before merge
(0,1) (1,1) (0,2) (1,2) (0,3) : after merge

См. также

ranges::merge
(C++20)
объединяет два отсортированных диапазона
(объект-функция алгоритма)
ranges::set_union
(C++20)
вычисляет объединение двух множеств
(объект-функция алгоритма)
ranges::is_sorted
(C++20)
проверяет, отсортирован ли диапазон
(объект-функция алгоритма)
ranges::sort
(C++20)
сортирует диапазон элементов
(объект-функция алгоритма)
inplace_merge
объединяет два упорядоченных диапазона на месте
(шаблон функции)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/algorithm/ranges/inplace_merge

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API