C++ 从c+中删除已知的项目子集+;矢量

C++ 从c+中删除已知的项目子集+;矢量,c++,vector,boost,stl,C++,Vector,Boost,Stl,鉴于: struct项{int id;…} std::向量项 std::vector idsToRemove 编写执行删除(同时保留顺序)的代码的最有效/最干净的方法是什么 使用删除\u如果,这可能是: items.erase(std::remove_if(items.begin(), items.end(), [&](const Item& i) { return std::find(idsToRemove.begin(), idsToRemove.

鉴于:

  • struct项{int id;…}
  • std::向量项
  • std::vector idsToRemove
编写执行删除(同时保留顺序)的代码的最有效/最干净的方法是什么

使用
删除\u如果
,这可能是:

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的一个子集,它们在
项中出现的顺序相同,而且两个数组都很小。如果有合适的方法,我可以使用Boost算法。)

由于已知
idsToRemove
中的ID位于
项中,且顺序相同,因此您可以在
项中使用两个迭代器来跟踪当前比较元素、当前目标以及遍历
idsToRemove
项,比较这两个元素,移动那些你想保留的。在这个过程结束时,调整大小>代码>项目<代码>是新的更小的大小。

我不认为这是对问题的真实回答(因为它移动了GoalPoST),但这是我在调查它时发现的东西,它可能对将来的搜索有用。 如果您没有从外部传入
idsToRemove
,但仍然需要遍历
项来决定要删除哪些项,那么有一种相当好的方法可以在O(N)中执行此操作:

#包括
boost::range::remove_erase_if(items,[&](const Item&Item)
{
//做任何你想做的事情
//返回true以删除该项,或
return false;//保存它
});

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

我想您的元素有唯一的ID,所以与其将要删除的元素存储在
std::vector
中,不如将它们存储在
std::unordered_集合中

这样,
std::remove_如果
方式非常干净:

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));
struct项{
int-id;
};
// ...
向量项;
std::无序设置idsToRemove;
项目。删除(
std::删除_if(std::开始(项目),std::结束(项目),[&](项目常量和it){
return(idsToRemove.find(it.id)!=std::end(idsToRemove));
}),
标准:结束(项目));

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

您可以将擦除移除限制在由与下一个ID匹配的第一个项目分隔的子范围内。如果您从后面执行此操作,则可以将移动量降至最低。不,如上所述,数组通常很小。我主要寻求代码的简洁性,但我也不希望它毫无必要地低效。如果它是大型任意列表,我会制作一个IDStoreReve的哈希或bloom过滤器,这样您可以在迭代项目时在O(1)时间内检查它们。但考虑到相同的顺序子集保证,这是完全不必要的。既然你说向量很小,O(NM)也没那么糟糕,但我投票赞成1201程序的答案。这是空间和时间,但你得自己写。我认为您上面的快速解决方案的真正问题是,从向量中删除一组单个项本身就是一个缓慢的过程。这听起来像是编写一个自定义的
remove\u if
。我主要是想看看是否有一个现有的STL或Boost算法或组合已经从向量中删除了向量(这似乎应该是常见的,不是吗?)(在到达
idsToRemove
的末尾后,您将比较
项中的尾部元素,并且需要在对迭代器进行去帧之前检查该结尾)。这里的关键洞察是,您可以对项目进行一次迭代,并且可以通过一次迭代将其与IDStoreRemove进行比较。您从不备份,因为您知道它是按相同顺序排列的子集。我被多个迭代器搞糊涂了,但您所说的是使用原始向量对内容进行无序排列,这样您就永远不会受到攻击vector delete的性能冲击。(我花了一分钟才明白你说的话。)@米拉尔:实际上STL并没有提供这样的功能,因为它并不常见。有一个向量是另一个向量的子集,并且额外保证元素的顺序与第一个相同,这是一种边缘情况。在任何情况下,将两个迭代器(因此效率更高)保留在几行代码中都是非常简单的。
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));