假设我有两个多重集。我想删除第一个多集中出现在第二个多集中的所有元素,遵循每个多集中出现的每个元素的次数。例如,如果多重集a
包含1
五次,多重b
两次,则在我计算a -= b
时,只应移除两个1
个实例来自a
。
以下是一些实现此目的的代码:
multiset<int> a;
multiset<int> b;
// remove all items that occur in b from a, respecting count ("a -= b")
for (multiset<int>::iterator i = b.begin(); i != b.end(); i++) {
if (a.count(*i) < 1) {
// error
}
// a.erase(*i) would remove ALL elements equal to *i from a, but we
// only want to remove one. a.find(*i) gives an iterator to the first
// occurrence of *i in a.
a.erase(a.find(*i));
}
当然有更好/更惯用的方法吗?
答案 0 :(得分:10)
虽然std::set_difference
要求您将元素放入新的集合中,但您仍然可以通过将元素从原始集合移动到新集合中然后交换两者来优化它(好吧,对于int
移动不是必要的,但这样算法保持灵活和通用)。
std::multiset<int> c;
std::set_difference(std::make_move_iterator(a.begin()),
std::make_move_iterator(a.end()),
b.begin(), b.end(),
std::inserter(c, c.begin()));
a.swap(c);
不是完全就地,但几乎并且仍然非常惯用,而且复杂性是线性的(因为std::insert_iterator
将始终为std::multiset::insert
提供正确的提示。)< / p>
答案 1 :(得分:3)
它也适用于多重集合。
来自最新的草案n3485 25.4.5.4 [set.di ff erence]
Remarks: If [first1,last1) contains m elements that are equivalent to each other and
[first2, last2) contains n elements that are equivalent to them, the last max(m−n,0)
elements from [first1, last1) shall be copied to the output range.
答案 2 :(得分:2)
由于容器是有序的,你可以一次迭代这两个容器,跳过只在一个容器中的值;类似的东西:
for (auto i = a.begin, j = b.begin(); i != a.end() && j != b.end();) {
if (*i < *j) {
++i;
} else if (*j < *i) {
++j;
} else {
i = a.erase(i);
++j;
}
}
这具有线性复杂性,避免了对count
和find
的对数时间调用。
或者,如果您不介意将要保留的元素移动到新地图中,则可以使用std::set_difference
。这可能不是线性时间,因为每个元素都需要插入到输出映射中。
更新:在进一步调查时,std::set_difference
似乎是线性的,因为在给出合适的提示时插入必须是线性的,insert_iterator
将提供正确的提示。如果你不介意构建一个新的multiset的额外内存使用,那么这可能被认为是更惯用的。
答案 3 :(得分:2)
使用std::set_difference
,但它会创建一个新的第三个集合:
std::multiset<int> v1{1, 2, 5, 5, 5, 9};
std::multiset<int> v2{2, 5, 7};
std::multiset<int> diff;
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(),
std::inserter(diff, diff.begin()));
输出
1 5 5 9