【问题标题】:Removing elements from the end of a vector in C++ using iterator使用迭代器从 C++ 中的向量末尾删除元素
【发布时间】:2014-04-12 23:19:33
【问题描述】:

我想以相反的顺序从向量中删除元素,从最后一个元素开始。据我所知,使用迭代器最简单的方法是:

std::vector<int> data = ...
std::vector<int>::iterator iter;
for(iter = data.end() - 1; iter != data.begin() - 1; iter--) {
    data.erase(iter);
}

我的问题是,这些 erase() 调用中的每一个是否都是 O(1),因为我们在最后删除,即使删除的一般复杂性是 O(n)?另外,假设向量至少有 1 个元素,这段代码是否“安全”?

【问题讨论】:

  • data.erase(data.rbegin(), data.rend()) 怎么样?
  • 您的实际问题是什么?整数向量的顺序无关紧要。

标签: c++ vector iterator time-complexity invalidation


【解决方案1】:

不,您的算法有 UB。 如果不是,它将有 O(n)。
将正文更改为此将删除 UB:iter = data.erase(iter);
为什么?因为vector.erase() 的行为是这样的:

  • 效果:在擦除点或擦除点之后使迭代器和引用无效。
  • 复杂性:T的析构函数被调用的次数等于元素个数 被擦除,但 T 的移动赋值运算符被称为次数等于 删除元素之后的向量中的元素。

如果你像这样重写循环,它甚至可以用于空容器:

for(auto it = data.end(); it!=data.begin(); it = data.erase(it-1)) /**/;

等于:

std::remove_if(data.rbegin(), data.rend(), [](data::reference x){return true;});

无论如何,使用data.clear()data.resize(0) 或与临时空向量进行交换很可能会更快。

【讨论】:

  • 如果我想在迭代过程中根据条件“跳过”一些元素怎么办?我意识到这将不再是 O(n) 算法,但我可以简单地使用 if(condition) it = data.erase(it-1); else it-- 并保持迭代器位置一致吗?
  • 是的,尽管这会使您的算法成为二次方:O(n²)。您最好使用反向迭代器调用std::remove_if()。这是保证O(n)
  • 谢谢! remove_if 看起来像是一种更简洁的方式来做我想做的事情——但是有没有办法检索 remove_if 删除了哪些项目以供参考?
  • 你可以对谓词中的当前元素做任何你想做的事情。甚至将其复制到其他地方以妥善保管。
  • @donnyton 不,它们被覆盖了。你真的要分割向量吗?
【解决方案2】:

除非您真的需要以相反的顺序删除它们,否则最好直接调用data.clear()

否则,不,它不是“安全的”:形成data.begin() - 1 是未定义的,并且在您调用.erase 之后递减iter 也是未定义的。 Deduplicator has posted a good fix for that.

【讨论】:

    【解决方案3】:

    有点。 std::vector erase() 的复杂度是

    与删除(破坏)的元素数量加上最后一个删除(移动)元素之后的元素数量成线性关系。

    基于此来源:http://www.cplusplus.com/reference/vector/vector/erase/

    由于您只删除一个元素及其最后一个元素,因此它与 N=1 呈线性关系,可以解释为 O(1) 常数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-15
      • 2016-09-24
      • 1970-01-01
      • 1970-01-01
      • 2014-09-04
      相关资源
      最近更新 更多