Erasing elements from a vector
我想使用擦除方法从向量中清除一个元素。但这里的问题是,不能保证元素在向量中只出现一次。它可能会出现多次,我需要清除它们。我的代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; } |
这段代码显然崩溃了,因为我在迭代向量时更改了向量的结尾。实现这一目标的最佳方法是什么?也就是说,是否有任何方法可以做到这一点,而无需多次迭代向量或创建向量的另一个副本?
使用"删除/删除"习惯用法:
1 2 | std::vector<int>& vec = myNumbers; // use shorter name vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end()); |
发生的情况是,
调用erase将使迭代器失效,您可以使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void erase(std::vector<int>& myNumbers_in, int number_in) { std::vector<int>::iterator iter = myNumbers_in.begin(); while (iter != myNumbers_in.end()) { if (*iter == number_in) { iter = myNumbers_in.erase(iter); } else { ++iter; } } } |
或者可以使用std::remove_,如果与函数和std::vector::erase一起使用:
1 2 3 4 5 6 7 8 9 10 11 12 | struct Eraser { Eraser(int number_in) : number_in(number_in) {} int number_in; bool operator()(int i) const { return i == number_in; } }; std::vector<int> myNumbers; myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end()); |
在这种情况下,您可以使用std::remove来代替编写自己的函数:
1 2 | std::vector<int> myNumbers; myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end()); |
您可以使用索引访问进行迭代,
避免O(n^2)复杂性您可以使用两个索引,i-当前测试索引,j-索引存储下一项并在循环结束时存储向量的新大小。
代码:
1 2 3 4 5 6 7 8 9 | void erase(std::vector<int>& v, int num) { size_t j = 0; for (size_t i = 0; i < v.size(); ++i) { if (v[i] != num) v[j++] = v[i]; } // trim vector to new size v.resize(j); } |
在这种情况下,迭代器没有失效,复杂性是O(N),代码非常简洁,不需要编写一些助手类,尽管在某些情况下,使用助手类可以在更灵活的代码中受益。
此代码不使用
使用纯STL,您可以通过以下方式进行此操作(这类似于Motti的答案):
1 2 3 4 5 6 | #include void erase(std::vector<int>& v, int num) { vector<int>::iterator it = remove(v.begin(), v.end(), num); v.erase(it, v.end()); } |
根据您这样做的原因,使用std::set可能比std::vector更好。
它只允许每个元素出现一次。如果您多次添加它,则仍将只有一个实例要擦除。这将使擦除操作变得简单。与向量相比,擦除操作的时间复杂度也较低,但是,在集合中添加元素的速度较慢,因此这可能不是什么优势。
如果你对元素被添加到向量中的次数或者元素被添加的顺序感兴趣的话,这当然不会起作用。