В чем разница между 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
.