在查看某些代码时,我发现std :: set_difference的循环和算法缓慢的实现 :
for(int i = 0; i < a.size(); i++)
{
iter = std::find(b.begin(),b.end(),a[i]);
if(iter != b.end())
{
b.erase(iter);
}
}
可以很容易地用sort(向量没有排序)+ set_difference来替换,但是这需要分配新的内存(参见我最近的Q Can output of set difference be stored in first input?为什么它不能完成“inplace”)。
所以我的解决方案就像:
sort(a.begin(), a.end());
for(size_t i = 0; i < b.size(); i++)
{
if (binary_search(a.begin(), a.end(), b[i]))
{
swap(b[i], b[b.size()-1]); //remove current element by swapping with last
b.pop_back(); // and removing new last by shrinking
}
}
可以更优雅地完成吗? 优雅是主观的,所以在这个Q的范围内被定义为更清晰的代码(理想情况下来自STL算法,但我认为它无法完成),但没有内存分配,也没有增加alg复杂性。
答案 0 :(得分:9)
这个在O(N + M)中进行,假设两个数组都已排序。
auto ib = std::begin(two);
auto iter = std::remove_if (
std::begin(one), std::end(one),
[&ib](int x) -> bool {
while (ib != std::end(two) && *ib < x) ++ib;
return (ib != std::end(two) && *ib == x);
});
答案 1 :(得分:6)
排序b
,以便您可以二进制搜索它,以减少时间复杂度。然后使用erase-remove惯用法,以便丢弃a
中包含的b
中的所有元素:
sort( begin(b), end(b) );
a.erase( remove_if( begin(a),end(a),
[&](auto x){return binary_search(begin(b),end(b),x);}), end(a) );
当然,您仍然可以通过删除sort()
并将binary_search()
替换为find()
来简化时间复杂度以简化并减少代码:
a.erase( remove_if( begin(a),end(a),
[&](auto x){return find(begin(b),end(b),x)!=end(b);}), end(a) );
这是品味问题。在这两种情况下,您都不需要堆分配。顺便说一下,我使用的是lambda自动参数,它们是C ++ 14。一些编译器已经实现了诸如clang之类的功能。如果你没有这样的编译器,但只有C ++ 11,那么用容器的元素类型替换auto
。
顺便说一下,这段代码没有提到任何类型!您可以编写模板函数,因此它适用于所有类型。第一个变体需要b
的随机访问迭代,而第二个代码不需要。{{1}}。
答案 2 :(得分:3)
想到的一个解决方案是remove_if
和binary_search
的结合。它与手动循环解决方案实际上相同,但由于它使用了更多STL功能,因此可能更“优雅”。
sort(begin(b), end(b));
auto iter = remove_if(begin(a), end(a),
[](auto x) {
return binary_search(begin(b), end(b), x);
});
// Now [begin(a), iter) defines a new range, and you can erase them however
// you see fit, based on the type of a.
答案 3 :(得分:1)
目前的代码非常清楚,因为任何程序员都应该明白发生了什么。
目前的表现是O(a.size() * b.size()
),根据实际尺寸,这可能会非常糟糕。
更简洁和类似STL的描述方法是使用remove_if和谓词,该谓词告诉你a中的值是否为。
b.erase(std::remove_if(b.begin(), b.end(), [](const auto&x) {
return std::find(a.begin(), a.end(), x) != a.end();
}), b.end());
(未经过测试,因此我可能会出现语法错误。)我使用了lambda,但如果您不使用C ++ 11编译器,则可以创建一个仿函数。
请注意,原始代码只删除了b
中a
中的一个值实例。我的解决方案将从b
中删除此类值的所有实例。
请注意,find
操作会一次又一次地发生,因此最好在较小的矢量上执行此操作以获得更好的参考局部性。
答案 4 :(得分:0)
vector<int64_t> a{3,2,7,5,11,13}, b{2,3,13,5};
set<int64_t> bs(b.begin(), b.end());
for (const auto& num: bs)
cout << num << " ";
cout << endl;
for (const auto& num: a)
bs.erase(num);
vector<int64_t> result(bs.begin(), bs.end());
for (const auto& num: result)
cout << num << " ";