vector::erase 删除单个或连续元素时应避免边遍历边用递增下标删除,正确做法是使用迭代器删除后接收返回值,或逆向遍历、标记+erase-remove惯用法。
直接调用 erase 是最直观的删除方式,但它会移动后续所有元素,时间复杂度是 O(n)。删末尾最快,删开头或中间代价高。
常见错误是边遍历边 erase 还用递增下标,导致跳过下一个元素:
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] == 42) v.erase(v.begin() + i); // ❌ 错!i 自增后指向原 i+2 位置
}
正确做法是使用返回的迭代器(erase 返回删除位置之后的合法迭代器):
for (auto it = v.begin(); it != v.end(); ) {
if (*it == 42) it = v.erase(
it); // ✅ 返回新 it,不自增
else ++it;
}
erase(pos) 删除单个元素,返回后继迭代器erase(first, last) 删除区间,返回 last
std::remove 是个“逻辑删除”算法:它把不满足条件的元素前移,返回一个新逻辑终点迭代器,但容器大小不变,后面是未定义值(通常是原尾部残留)。
典型误用:
std::remove(v.begin(), v.end(), 42); // ❌ 容器 size 没变,数据没真删
// 此时 v 可能变成 {1,2,3,1,2,3,?, ?, ?},? 是脏数据
必须配合 erase 才算真正删除(即“erase–remove 惯用法”):
v.erase(std::remove(v.begin(), v.end(), 42), v.end()); // ✅ 真删
std::remove 是稳定算法,保持剩余元素相对顺序==
vector 来说,它比多次 erase 快得多(一次移动,O(n);多次 erase 是 O(n²))当删除逻辑不是简单相等(比如删偶数、删空字符串、删超时对象),就得用 std::remove_if:
v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end());
注意 lambda 捕获和生命周期:如果要访问外部变量(如阈值、容器引用),需显式捕获,避免悬垂引用。
operator== 或谓词逻辑正确,尤其注意 const 成员函数修饰频繁在头部或中部删除,vector 不是最佳选择——每次 erase 都触发大量内存拷贝。此时应考虑 std::list 或 std::deque(后者支持高效首尾删,但中部删仍慢)。
另一个易忽略点:erase 后立即访问 v.back() 或 v[v.size()-1] 前,务必检查 v.empty(),否则越界未定义行为。
erase 不改变容量(v.capacity() 不变),要用 shrink_to_fit() 提议释放(不保证执行)erase 和 remove 都非线程安全,需外部同步std::unique 去重时,必须先排序,否则只删相邻重复项