C++ 什么';std::merge和std::set_union之间的区别是什么?
问题很清楚,我的google-and-cplusplus.com/reference-fu让我失望。C++ 什么';std::merge和std::set_union之间的区别是什么?,c++,merge,C++,Merge,问题很清楚,我的google-and-cplusplus.com/reference-fu让我失望。std::merge合并所有元素,但不消除重复项,而std::set\u union消除重复项。也就是说,后者应用了的操作规则。std::merge在输出中保留两个范围中的所有元素,第一个范围中的等效元素在第二个范围中的等效元素之前。当两个范围中都出现等效元素时,std::set_union仅从第一个范围中获取元素,否则每个元素按std::merge的顺序合并 参考文献:ISO/IEC 14882
std::merge
合并所有元素,但不消除重复项,而std::set\u union
消除重复项。也就是说,后者应用了的操作规则。std::merge
在输出中保留两个范围中的所有元素,第一个范围中的等效元素在第二个范围中的等效元素之前。当两个范围中都出现等效元素时,std::set_union
仅从第一个范围中获取元素,否则每个元素按std::merge
的顺序合并
参考文献:ISO/IEC 14882:2003 25.3.4[lib.alg.merge]和25.3.5.2[lib.set.union]。将包含两个集合中仅存在一次的元素。将包含它们两次
例如,使用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';
}
这是我在我发布到已接受答案的评论中建议的验证(即,如果一个元素出现在其中一个输入集中N次,它将出现在set_union的输出中N次-因此set_union不会以我们“自然”或“数学”预期的方式删除重复的等效项-但是,如果两个输入范围只包含一次公共项,则set_union将似乎删除du折叠的
#包括
#包括
#包括
#包括
使用名称空间std;
无效打印机(int i){cout添加到前面的答案中-注意std::set_union
的复杂性是std::merge
的两倍。实际上,这意味着std::set_union
中的比较器可以在元素被取消引用后应用于该元素,而std::merge
中的比较器永远不会是这种情况
为什么这很重要?考虑一些类似的事情:
std::vector<Foo> lhs, rhs;
但是现在假设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
具有相同的复杂性,这听起来更像是交叉点,不是吗?@davka:我说的是在第二个范围中存在等价物的行为。我认为其他范围中没有等价物的所有元素都会被保留。我已经澄清了我的措辞。好的,在读了这句话之后nce 5次:)我明白你的意思。我把它理解为“只需要”…@davka:请仔细阅读,我说的“只需要”,我认为更清楚。我会尽力:)我认为你的新措辞更清楚。对我们许多人来说,英语是第二(或第N)位语言std::merge
也可以处理排序范围并生成排序结果。@Charkes Bailey:谢谢,我没有检查std::merge,也不认为它会这样做。修改了我的答案。为了每个人的利益,也许是我太挑剔了,但上面的内容不够清晰,我不喜欢。阅读这个答案可能会让你相信duplicate由set_union()消除-它们是消除的,但不一定以您可能认为的方式。如果第一个范围多次包含等效元素,则该元素将在输出范围中出现相同的次数。这很容易验证:(很抱歉,stackoverflow仍然是新手,该验证无法在注释中显示为代码,因此我创建了一个包含详细信息的新答案)我同意这是一个非常不清楚的答案。这意味着集合\并集实际上是一个交集,而不是一个并集。@Emilie:正如我在问题中所说的,这并没有给我提供答案。为什么我只想用一个std::set
…感谢我眼中最简洁明了的答案。
std::set_union(std::cbegin(lhs), std::cend(lhs),
std::cbegin(rhs), std::cend(rhs),
std::back_inserter(union));
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));
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));