【问题标题】:How to delete an element from a vector while looping over it?如何在循环遍历向量时从向量中删除元素?
【发布时间】:2011-12-21 22:28:36
【问题描述】:

我正在循环一个带有循环的向量,例如for(int i = 0; i < vec.size(); i++)。在这个循环中,我检查该向量索引处元素的条件,如果某个条件为真,我想删除该元素。

如何在不崩溃的情况下循环删除矢量元素?

【问题讨论】:

标签: c++


【解决方案1】:

从 STL 容器中删除满足给定谓词的所有元素的惯用方法是使用 remove-erase idiom。这个想法是将谓词(即对某些元素产生真或假的函数)移动到给定的函数中,比如pred,然后:

static bool pred( const std::string &s ) {
  // ...
}

std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );

如果你坚持使用索引,你不应该为每个元素增加索引,而只为那些没有被删除的元素增加索引:

std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
    if ( shouldBeRemoved( v[i] ) ) {
        v.erase( v.begin() + i );
    } else {
        ++i;
    }
}

但是,这不仅代码更多,惯用语更少(阅读:C++ 程序员实际上必须查看代码,而“擦除和删除”习语立即给出了一些想法),而且效率也低得多,因为向量将它们的元素存储在一个连续的内存块中,因此擦除向量末端以外的位置也会将擦除段之后的所有元素移动到它们的新位置。

【讨论】:

  • 如何知道在 v.erase() 之后你杀死了多少元素? (不计算前后大小?)
  • 谓词不必是函数,而可以是函子(尤其是当您需要获取外部数据来决定是否删除元素时)。
  • @dynamic std::vector 具有随机访问迭代器,因此您可以通过从当前结束迭代器中减去std::remove_if 返回的新结束迭代器(例如v.end() )。
【解决方案2】:

如果您不能使用删除/擦除(例如,因为您不想使用 lambda 或编写谓词),请使用标准习惯用法来删除序列容器元素:

for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
    if (delete_condition)
    {
        it = v.erase(it);
    }
    else
    {
        ++it;
    }
}

如果可能的话,最好删除/擦除:

#include <algorithm>

v.erase(std::remove_if(v.begin(), v.end(),
                       [](T const & x) -> bool { /* decide */ }),
        v.end());

【讨论】:

  • for (auto it . . . 这会编译吗?
【解决方案3】:

使用Erase-Remove Idiom,使用带有谓词的remove_if 来指定您的条件。

【讨论】:

  • 我们可以保留链接到 SO。在这里被破坏的可能性较小。
【解决方案4】:
if(vector_name.empty() == false) {
    for(int i = vector_name.size() - 1; i >= 0; i--)
    {
        if(condition) 
            vector_name.erase(vector_name.at(i));
    }
}

这对我有用。并且不需要考虑索引已经被删除了。

【讨论】:

  • 应该擦除(vector_name.begin() + i);检查向量是否为空也是没用的,因为 size == 0 会导致 for 循环中没有迭代
  • @log0 随机访问是可能的。为什么不?并同意后者。
  • erase 将迭代器作为参数,at 返回一个向量(例如字符串)
【解决方案5】:

向后迭代向量。这样一来,您就不会失去访问尚未访问的元素的能力。

【讨论】:

    【解决方案6】:

    我知道您是在专门询问从向量中删除的问题,但只是想指出从 std::vector 中删除项目的成本很高,因为必须将删除项目之后的所有项目复制到新位置。如果要从容器中删除项目,则应使用 std::list。 std::list::erase(item) 方法甚至返回指向刚刚擦除后的值的迭代器,因此它很容易在 for 或 while 循环中使用。 std::list 的另一个好处是指向未擦除项目的迭代器在整个列表存在期间保持有效。例如参见docs at cplusplus.com

    也就是说,如果你别无选择,一个可行的技巧就是创建一个新的空向量并从第一个向量向其中添加项目,然后使用 std::swap(oldVec, newVec),这非常高效(没有副本,只是更改内部指针)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2012-01-22
      • 2015-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多