[英]What's the difference between std::merge and std::set_union?
问题很明确,我的 google- 和 cplusplus.com/reference-fu 让我失望了。
std::set_union
将包含那些只出现在两个集合中一次的元素。 std::merge
将包含它们两次。
例如, A = {1, 2, 5}; B = {2, 3, 4}
A = {1, 2, 5}; B = {2, 3, 4}
:
C = {1, 2, 3, 4, 5}
D = {1, 2, 2, 3, 4, 5}
两者都适用于已排序的范围,并返回已排序的结果。
简短示例:
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
std::set<int> A = {1, 2, 5};
std::set<int> B = {2, 3, 4};
std::vector<int> out;
std::set_union(std::begin(A), std::end(A), std::begin(B), std::end(B),
std::back_inserter(out));
for (auto i : out)
{
std::cout << i << " ";
}
std::cout << '\n';
out.clear();
std::merge(std::begin(A), std::end(A), std::begin(B), std::end(B),
std::back_inserter(out));
for (auto i : out)
{
std::cout << i << " ";
}
std::cout << '\n';
}
输出:
1 2 3 4 5
1 2 2 3 4 5
std::merge
保留来自两个范围的所有元素,来自第一个范围的等效元素在输出中来自第二个范围的等效元素之前。 如果等效元素出现在两个范围内, std::set_union
仅采用第一个范围内的元素,否则每个元素按顺序std::merge
。
参考文献:ISO/IEC 14882:2003 25.3.4 [lib.alg.merge] 和 25.3.5.2 [lib.set.union]。
这是我在发布到已接受答案的评论中建议的验证(即,如果一个元素在其中一个输入集中出现 N 次,它将在 set_union 的输出中出现 N 次 - 所以 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:: 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
具有相同的复杂性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.