如何在迭代时从向量中删除元素?
我想使用擦除方法从向量中清除元素。 但这里的问题是,不能保证该元素在向量中只出现一次。 它可能会出现多次,我需要清除所有这些。 我的代码是这样的:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
std::vector<int>::iterator endIter = myNumbers_in.end();
for(; iter != endIter; ++iter)
{
if(*iter == number_in)
{
myNumbers_in.erase(iter);
}
}
}
int main(int argc, char* argv[])
{
std::vector<int> myNmbers;
for(int i = 0; i < 2; ++i)
{
myNmbers.push_back(i);
myNmbers.push_back(i);
}
erase(myNmbers, 1);
return 0;
}
这段代码显然会崩溃,因为我在迭代向量时更改了向量的末尾。 实现这一目标的最佳方法是什么? 即,有没有一种方法可以做到这一点,而无需多次迭代向量或创建向量的另一个副本?
I want to clear a element from a vector using the erase method. But the problem here is that the element is not guaranteed to occur only once in the vector. It may be present multiple times and I need to clear all of them. My code is something like this:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
std::vector<int>::iterator endIter = myNumbers_in.end();
for(; iter != endIter; ++iter)
{
if(*iter == number_in)
{
myNumbers_in.erase(iter);
}
}
}
int main(int argc, char* argv[])
{
std::vector<int> myNmbers;
for(int i = 0; i < 2; ++i)
{
myNmbers.push_back(i);
myNmbers.push_back(i);
}
erase(myNmbers, 1);
return 0;
}
This code obviously crashes because I am changing the end of the vector while iterating through it. What is the best way to achieve this? I.e. is there any way to do this without iterating through the vector multiple times or creating one more copy of the vector?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您可以使用索引访问进行迭代,
避免 O(n^2) 复杂度
您可以使用两个索引,i - 当前测试索引,j - 索引
存储下一项,并在循环结束时存储新的向量大小。
代码:
在这种情况下,您不会使迭代器失效,复杂度为 O(n),并且代码非常简洁,您不需要编写一些辅助类,尽管在某些情况下使用辅助类可以受益于更灵活的代码。
此代码不使用
erase
方法,但解决了您的任务。使用纯 stl,您可以通过以下方式执行此操作(这类似于 Motti 的答案):
You can iterate using the index access,
To avoid O(n^2) complexity
you can use two indices, i - current testing index, j - index to
store next item and at the end of the cycle new size of the vector.
code:
In such case you have no invalidating of iterators, complexity is O(n), and code is very concise and you don't need to write some helper classes, although in some case using helper classes can benefit in more flexible code.
This code does not use
erase
method, but solves your task.Using pure stl you can do this in the following way (this is similar to the Motti's answer):
根据您这样做的原因,使用 std::set 可能是比 std::vector 更好的主意。
它允许每个元素仅出现一次。 如果多次添加,则无论如何都只会删除一个实例。 这将使擦除操作变得微不足道。
擦除操作的时间复杂度也比向量低,但是,在集合上添加元素速度较慢,因此可能没有太大优势。
如果您对元素添加到向量中的次数或添加元素的顺序感兴趣,这当然不起作用。
Depending on why you are doing this, using a std::set might be a better idea than std::vector.
It allows each element to occur only once. If you add it multiple times, there will only be one instance to erase anyway. This will make the erase operation trivial.
The erase operation will also have lower time complexity than on the vector, however, adding elements is slower on the set so it might not be much of an advantage.
This of course won't work if you are interested in how many times an element has been added to your vector or the order the elements were added.
自 有 std::erase 和 std::erase_if >C++20 结合了删除-擦除习惯用法。
或者
There are std::erase and std::erase_if since C++20 which combines the remove-erase idiom.
or
如果您按如下方式更改代码,则可以进行稳定的删除。
然而,也可以使用诸如以下的方法。
如果我们必须保留序列的顺序(例如,如果我们按照某些有趣的属性对其进行排序),那么我们可以使用上述方法之一。 但是,如果序列只是一袋值,我们根本不关心其顺序,那么我们可能会考虑从序列末尾移动单个元素来填充创建时的每个新间隙:
以下是它们的基准结果:
CLang 15.0:
Gcc 12.2:
If you change your code as follows, you can do stable deletion.
However, a method such as the following can also be used.
If we must preserve our sequence’s order (say, if we’re keeping it sorted by some interesting property), then we can use one of the above. But if the sequence is just a bag of values whose order we don’t care about at all, then we might consider moving single elements from the end of the sequence to fill each new gap as it’s created:
Below are their benchmark results:
CLang 15.0:
Gcc 12.2:
您可以使用
find
以及随后的erase
方法来删除特定元素。例如:
You can use the
find
and consequently theerase
method to remove a specific element.For example:
调用擦除将使迭代器无效,您可以使用:
或者您可以使用 std::remove_if 与函子和 std::vector::erase 一起使用:
在这种情况下,您可以使用 std::remove:
在 C++11 中,您可以使用 lambda 而不是函子:
在 C++17 中 std::experimental::erase 和 std::experimental::erase_if 也可用,在 C++20 中它们(最终)重命名为 std::erase 和 std::erase_if(注意:在 Visual Studio 2019 中,您需要将 C++ 语言版本更改为最新的实验版本以获得支持):
或者:
Calling erase will invalidate iterators, you could use:
Or you could use std::remove_if together with a functor and std::vector::erase:
Instead of writing your own functor in this case you could use std::remove:
In C++11 you could use a lambda instead of a functor:
In C++17 std::experimental::erase and std::experimental::erase_if are also available, in C++20 these are (finally) renamed to std::erase and std::erase_if (note: in Visual Studio 2019 you'll need to change your C++ language version to the latest experimental version for support):
or:
从 C++20 开始,有独立的
std::erase
和std::erase_if
函数适用于容器并大大简化事情:在 C++20 之前,使用 擦除删除习惯用法:
发生的情况是
std::remove
压缩与值不同的元素被删除(number_in
)在向量
的开头,并将迭代器返回到该范围之后的第一个元素。 然后erase
删除这些元素(其值未指定)。Since C++20, there are freestanding
std::erase
andstd::erase_if
functions that work on containers and simplify things considerably:Prior to C++20, use the erase-remove idiom:
What happens is that
std::remove
compacts the elements that differ from the value to be removed (number_in
) in the beginning of thevector
and returns the iterator to the first element after that range. Thenerase
removes these elements (whose value is unspecified).