从c++向量中删除一个已知的项子集

Removing a known subset of items from a c++ vector

本文关键字:一个 子集 向量 c++ 删除      更新时间:2023-10-16

给定:

  • struct Item { int id; ... };
  • std::vector<Item> items;
  • std::vector<int> idsToRemove;

编写执行删除(同时保留顺序(的代码的最有效/最干净的方法是什么?

使用remove_if,这可能是:

items.erase(std::remove_if(items.begin(), items.end(),
    [&](const Item& i) {
        return std::find(idsToRemove.begin(), idsToRemove.end(), i.id)
            != idsToRemove.end();
    }), items.end());

另一种方式可能是:

for (auto id : idsToRemove)
{
    items.erase(std::remove(items.begin(), items.end(), id), items.end());
    // or items.erase(std::find(items.begin(), items.end(), id));
    // provided that we know id always exists in items
}

这两个都感觉不太好(它们看起来都O(N*M((,尽管第二个看起来比第一个整洁。有更好的方法吗?

(如果有帮助的话,虽然两个向量都没有排序,但已知idsToRemove是ID的子集,其顺序与它们在items中出现的顺序相同,并且两个数组都很小。如果有合适的算法,我可以使用Boost算法。(

由于已知idsToRemove中的id在items中,并且顺序相同,因此可以在items中使用几个迭代器来跟踪当前比较元素、当前目的地,并遍历idsToRemoveitems,比较这两个元素,移动要保留的元素。在该过程结束时,将items的大小调整为新的较小大小。

我不认为这是对所述问题的真正答案(因为它移动了门柱(,但这是我在调查它时发现的,它可能对未来的搜索有用。

如果您没有从外部传入idsToRemove,但无论如何都需要遍历items来决定删除哪个,那么在O(N(中有一种相当好的方法:

#include <boost/range/algorithm_ext/erase.hpp>
boost::range::remove_erase_if(items, [&](const Item& item)
{
    // do whatever else you want to item
    // return true to erase the item, or
    return false; // to keep it
});

在内部,它基于std::remove_if,但它更整洁,类似于基于的范围。

我假设您的元素具有唯一的ID,因此,与其将要删除的元素存储在std::vector中,不如将它们存储在std::unordered_set中。

通过这种方式,std::remove_if方式真的很干净:

struct Item {
    int id;
};
// ...
std::vector<Item> items;
std::unordered_set<int> idsToRemove;
items.erase(
    std::remove_if(std::begin(items), std::end(items), [&](Item const& it) {
            return (idsToRemove.find(it.id) != std::end(idsToRemove));
        }),
    std::end(items));

复杂度(摊销(将为O(N),其中N是向量中元素的数量。