【问题标题】:Remove elements from vector on given indexes, order does not matter从给定索引的向量中删除元素,顺序无关紧要
【发布时间】:2014-09-01 08:45:55
【问题描述】:

我拥有的是元素的向量,我不关心它们的顺序。 比我有N 要从向量中删除的元素的索引(每个索引在向量中的唯一位置)。我希望尽快删除。

我能想到的最好办法是将索引存储在集合中(顺序索引):

std::set<unsigned int> idxs;
for (int i=0; i<N; ++i)
    idxs.insert(some_index);

然后以相反的顺序迭代集合并用向量的最后一个元素替换要删除的索引。

std::set<unsigned int>::reverse_iterator rit;
for (rit = idxs.rbegin(); rit != idxs.rend(); ++rit) {
    vec[*rit].swap(vec[vec.size() - 1]);
    vec.resize(vec.size() - 1);
}

但是我在想是否有更有效的方法来做到这一点,因为 set 的使用对我来说似乎有点矫枉过正,我很想避免排序全部。

EDIT1: 让我们假设我使用向量并在之后对其进行排序。

std::vector<unsigned int> idxs;
for (int i=0; i<N; ++i)
    idxs.push_back(some_index);
std::sort(idxs.begin(), idxs.end());

我可以再推它吗?

EDIT2: 我应该提到该向量将包含多达 10 个元素。但是,我的程序中的删除经常发生(数十万次)。

【问题讨论】:

  • 通常当你想要排序时,你使用std::sort
  • 我能想到的 2 种方法:1. 迭代并创建不在索引列表中的元素的新向量。 2.定义一个仿函数并使用erase_if并调用您的仿函数,它将比较索引并以这种方式擦除。
  • @EdChum 没有 erase_if 这样的东西。
  • @T.C.对不起remove_if
  • 这些向量有多大,如果它们很小,那么从有效索引创建一个新向量应该是微不足道和快速的

标签: c++ vector


【解决方案1】:

set 是个不错的选择。我猜使用另一个分配器(例如 arena)会产生最大的影响。为什么不使用集合而不是元素向量作为开头?

我看到以下相关变化:

  • 不要删除,而是创建一个新向量并复制保留的元素,然后交换回来。
    这使您的索引保持稳定(与删除不同,这需要对索引进行排序或更新)。

  • 不要使用索引向量,而是使用与数据长度相同的布尔向量。 给定“最大 10”的长度,位掩码似乎就足够了

所以,大致:

struct Index 
{
   DWORD removeMask = 0;  // or use bit vector for larger N
   void TagForRemove(int idx) { removeMask |= (1<<idx); }
   boll DoRemove(int idx) const { return (removeMask & (1<<idx)) != 0; }
}

// create new vector, or remove, as you like
void ApplyRemoveIndex(vector<T> & v, Index remove)
{
   vector<T> copy;
   copy.reserve(v.size());
   for (i=0..v.size())
     if (!remove.DoRemove(i))
       copy.push_back(v[i]);
   copy.swap(v);
}

【讨论】:

    【解决方案2】:

    您可以使用swap/pop_back 删除给定索引处的项目,并使用哈希表跟踪您已移动的索引。移除次数是线性空间和时间。

    std::vector<T> vec = ...;
    std::vector<unsigned int> idxs;
    std::unordered_map<unsigned int, unsigned int> map;
    
    for(auto index : idxs) {
      unsigned int trueIndex = index;
      while (trueIndex >= vec.size()) {
        trueIndex = map[trueIndex];
      }
    
      // element at index 'vec.size()-1' is being moved to index 'index'   
      map[vec.size()-1] = index; 
      swap(vec[trueIndex], vec[vec.size()-1]);
      vec.pop_back();   
    }
    

    【讨论】:

    • 我真的不认为你的代码是有效的,因为你根本不使用 trueIndex。但也许我错了。
    • 已更正。感谢您的发现。
    猜你喜欢
    • 2011-12-11
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2020-01-20
    • 1970-01-01
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多