В чем разница между std::merge и std::set union?

вопрос ясен, мой google-и cplusplus.com/reference-fu подводит меня.

5 ответов


set_union будет содержать только элементы, которые присутствуют в обоих наборах один раз. слияние будет содержать их дважды.

оба работают над отсортированными диапазонами и возвращают отсортированный результат.


std::merge сохраняет все элементы из обоих диапазонов, эквивалентные элементы из первого диапазона, предшествующие эквивалентным элементам из второго диапазона на выходе. Где эквивалентные элементы появляются в обоих диапазонах std::set_union принимает только элемент из первого диапазона, в противном случае каждый элемент объединяется в порядке, как с std::merge.

ссылки: ISO / IEC 14882:2003 25.3.4 [lib.АЛГ.merge] и 25.3.5.2 [lib.набор.объединение.]


std::merge объединяет все элементы, не исключая дубликатов, в то время как std::set_union исключает дубликаты. То есть последний применяет правило Союз операции теория множеств.


это проверка, которую я предложил в комментарии, который я разместил в принятом ответе (т. е. если элемент присутствует в одном из входных наборов N раз, он появится N раз в выходе set_union - так что set_union делает не удалите дубликаты эквивалентных элементов так, как мы ожидали бы "естественно" или "математически" - если, однако, оба входных диапазона содержат общий элемент только один раз, то set_union будет появляется для удаления двух экземплярах)

#include <vector>
#include <algorithm>
#include <iostream>
#include <cassert>

using namespace std;

void printer(int i) { cout << i << ", "; }

int main() {
    int mynumbers1[] = { 0, 1, 2, 3, 3, 4 }; // this is sorted, 3 is dupe
    int mynumbers2[] = { 5 };                // this is sorted


    vector<int> union_result(10);
    set_union(mynumbers1, mynumbers1 + sizeof(mynumbers1)/sizeof(int),
              mynumbers2, mynumbers2 + sizeof(mynumbers2)/sizeof(int),
              union_result.begin());
    for_each(union_result.begin(), union_result.end(), printer);

    return 0;
}

Это будет печать: 0, 1, 2, 3, 3, 4, 5, 0, 0, 0,


чтобы добавить к предыдущим ответам-остерегайтесь, что сложность std::set_union вдвое больше, чем std::merge. На практике это означает компаратор в std::set_union может применяться к элементу после он был разыменован, в то время как с std::merge это не тот случай.

почему это важно? Рассмотрим что-то вроде:

std::vector<Foo> lhs, rhs;

и вы хотите произвести Союза lhs и rhs:

std::set_union(std::cbegin(lhs), std::cend(lhs),
               std::cbegin(rhs), std::cend(rhs),
               std::back_inserter(union));

а теперь предположим, что Foo не копируется или очень дорого копировать, и вам не нужны оригиналы. Вы можете подумать, чтобы использовать:

std::set_union(std::make_move_iterator(std::begin(lhs)),
               std::make_move_iterator(std::end(lhs)),
               std::make_move_iterator(std::begin(rhs)),
               std::make_move_iterator(std::end(rhs)),
               std::back_inserter(union));

но это неопределенное поведение, так как есть возможность переехал Foo сравнивается! Поэтому правильным решением является:

std::merge(std::make_move_iterator(std::begin(lhs)),
           std::make_move_iterator(std::end(lhs)),
           std::make_move_iterator(std::begin(rhs)),
           std::make_move_iterator(std::end(rhs)),
           std::back_inserter(union));
union.erase(std::unique(std::begin(union), std::end(union), std::end(union));

который имеет ту же сложность, что и std::set_union.