如何通过索引从std :: vector <>中删除元素?

dau*_*man 464 c++ stl vector erase

我有一个std :: vector <int>,我想删除第n个元素.我怎么做?

std::vector<int> vec;

vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);

vec.erase(???);
Run Code Online (Sandbox Code Playgroud)

mmm*_*mmm 645

要删除单个元素,您可以执行以下操作:

std::vector<int> vec;

vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);

// Deletes the second element (vec[1])
vec.erase(vec.begin() + 1);
Run Code Online (Sandbox Code Playgroud)

或者,一次删除多个元素:

// Deletes the second through third elements (vec[1], vec[2])
vec.erase(vec.begin() + 1, vec.begin() + 3);
Run Code Online (Sandbox Code Playgroud)

  • 另请注意,二进制`operator +`是__not__必须为其他容器类型上的迭代器定义,比如`list <T> :: iterator`(你不能在`std :: list`上做`list.begin()+ 2`,你必须使用[`std :: advance`](http://www.cplusplus.com/reference/iterator/advance/) (38认同)
  • 感谢所有回答的人.当删除元素这样简单的操作时,我们怎么想类设计需要一个人来StackOverflow? (6认同)
  • @Caleth是的,但是 std::vector 仍然可以为这个非常常见的用例提供一种方法。每个人都抨击 Qt 容器,但例如 QList 具有 removeOne() ,与丑陋的 std::vector 相比,这只是一个简单的事情。 (5认同)
  • @Pierre,因为特定元素的数字索引不是访问的主要模型,迭代器则是。查看容器元素的所有函数都使用该容器的迭代器。例如[`std :: find_if`](https://en.cppreference.com/w/cpp/algorithm/find) (3认同)
  • @Caleth 嗯?通过索引(`at()` 和 `operator[]`)访问是 `std::vector` 的主要独特属性之一。只有同时支持在索引处插入和擦除才是一致的。 (3认同)
  • 提前,您必须将迭代器保存在变量中.如果你使用std :: next,你可以在一行中执行:vec.erase(next(begin(vec),123)); (2认同)

Cod*_*ddy 197

std :: vector上的erase方法被重载,因此调用它可能更清晰

vec.erase(vec.begin() + index);
Run Code Online (Sandbox Code Playgroud)

当你只想删除一个元素时.

  • 我希望有人会提到`vec.erase(0)`不起作用,但`vec.erase(vec.begin()+ 0)`(或没有+0).否则我没有匹配的函数调用,这就是我来到这里的原因 (23认同)
  • 如果只有一个元素,索引是0,那么你得到的`vec.begin()`是有效的. (15认同)
  • 但无论你有多少元素,这个问题都会出现. (3认同)
  • @qrtLs擦除()函数以迭代器类型作为参数;由于 0 不是迭代器,因此它会给出编译器错误,因为没有匹配的函数调用。 (2认同)

Max*_*Max 52

template <typename T>
void remove(std::vector<T>& vec, size_t pos)
{
    std::vector<T>::iterator it = vec.begin();
    std::advance(it, pos);
    vec.erase(it);
}
Run Code Online (Sandbox Code Playgroud)

  • @JoeyvG:由于`vector <T> :: iterator`是一个随机访问迭代器,你的版本很好,可能更清晰一些.但是,如果将容器更改为另一个不支持随机访问迭代器的容器,Max发布的版本应该可以正常工作 (12认同)
  • 这是 imo 更好的答案,因为它也适用于其他容器格式。您也可以使用 std::next()。 (3认同)
  • Max,是什么让这个函数更好:`template <typename T> void remove(std :: vector <T>&vec,size_t pos){vec.erase(vec.begin + pos); 我不是说要么更好,只是要求出于个人利益并回归这个问题可能得到的最好结果. (2认同)

Esw*_*ndi 13

erase方法将以两种方式使用:

  1. 擦除单个元素:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
    
    Run Code Online (Sandbox Code Playgroud)
  2. 擦除元素范围:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element
    
    Run Code Online (Sandbox Code Playgroud)

  • @AlastairG 这个答案比原始答案更短、更清晰,尽管从技术上来说它可能只是一个编辑(尽管这样的编辑可能违背原始答案OP的意愿) (7认同)
  • 这是接受答案将近7年之后的重复答案。请不要这样做。 (3认同)

小智 9

擦除带有索引的元素:

vec.erase(vec.begin() + index);
Run Code Online (Sandbox Code Playgroud)

擦除具有值的元素:

vec.erase(find(vec.begin(),vec.end(),value));
Run Code Online (Sandbox Code Playgroud)

  • 与其他现有的较旧的和已投票的答案相比,请使该答案提供的额外见解更加明显。 (3认同)

Var*_*arg 8

实际上,该erase功能适用于两个配置文件:

由于std :: vec.begin()标记容器的开头,如果我们想删除向量中的第i个元素,我们可以使用:

vec.erase(vec.begin() + index);
Run Code Online (Sandbox Code Playgroud)

如果仔细观察,vec.begin()只是指向我们向量的起始位置的指针,并且将i的值添加到它会将指针递增到i位置,所以我们可以通过以下方式访问指向第i个元素的指针:

&vec[i]
Run Code Online (Sandbox Code Playgroud)

所以我们可以写:

vec.erase(&vec[i]); // To delete the ith element
Run Code Online (Sandbox Code Playgroud)

  • -1最后一行无法编译(至少在VS2017中).该代码假定vector :: iterator可以从原始指针隐式构造,这是标准不需要的. (5认同)

Pie*_*ret 8

对某些人来说似乎很明显,但要详细说明上述答案:

如果您在整个向量的循环中std::vector使用删除元素erase,您应该以相反的顺序处理您的向量,也就是说使用

for (int i = v.size() - 1; i >= 0; i--)

而不是(经典的)

for (int i = 0; i < v.size(); i++)

原因是erase如果删除第 4 个元素,索引会受到影响,那么以前的第 5 个元素现在是新的第 4 个元素,如果您正在执行i++.

下面是一个简单的例子,说明了我想删除 int 向量的所有odds 元素;

#include <iostream>
#include <vector>

using namespace std;

void printVector(const vector<int> &v)
{
    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
    cout << endl;
}

int main()
{    
    vector<int> v1, v2;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
        v2.push_back(i);
    }

    // print v1
    cout << "v1: " << endl;
    printVector(v1);
    
    cout << endl;
    
    // print v2
    cout << "v2: " << endl;
    printVector(v2);
    
    // Erase all odd elements
    cout << "--- Erase odd elements ---" << endl;
    
    // loop with decreasing indices
    cout << "Process v2 with decreasing indices: " << endl;
    for (int i = v2.size() - 1; i >= 0; i--)
    {
        if (v2[i] % 2 != 0)
        {
            cout << "# ";
            v2.erase(v2.begin() + i);
        }
        else
        {
            cout << v2[i] << " ";
        }
    }
    cout << endl;
    cout << endl;
    
    // loop with increasing indices
    cout << "Process v1 with increasing indices: " << endl;
    for (int i = 0; i < v1.size(); i++)
    {
        if (v1[i] % 2 != 0)
        {
            cout << "# ";
            v1.erase(v1.begin() + i);
        }
        else
        {
            cout << v1[i] << " ";
        }
    }
    
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

v1:
0 1 2 3 4 5 6 7 8 9

v2:
0 1 2 3 4 5 6 7 8 9
--- Erase odd elements ---
Process v2 with decreasing indices:
# 8 # 6 # 4 # 2 # 0

Process v1 with increasing indices:
0 # # # # #
Run Code Online (Sandbox Code Playgroud)

请注意,在索引增加的第二个版本中,偶数不会显示,因为它们被跳过,因为 i++


小智 7

如果你有一个无序的向量,你可以利用它无序的事实,并使用我在CPPCON的Dan Higgins看到的东西

template< typename TContainer >
static bool EraseFromUnorderedByIndex( TContainer& inContainer, size_t inIndex )
{
    if ( inIndex < inContainer.size() )
    {
        if ( inIndex != inContainer.size() - 1 )
            inContainer[inIndex] = inContainer.back();
        inContainer.pop_back();
        return true;
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

由于列表顺序无关紧要,只需取出列表中的最后一个元素并将其复制到要删除的项目的顶部,然后弹出并删除最后一项.

  • 这完全需要作为“unordered_remove”和“unordered_remove_if”添加到标准库中......除非它已经被添加并且我错过了它,这种情况现在越来越频繁地发生:) (3认同)
  • 如果建议使用移动分配或交换而不是复制分配。 (2认同)

小智 6

如果您使用大向量(大小 > 100,000)并想要删除大量元素,我建议您执行以下操作:

int main(int argc, char** argv) {

    vector <int> vec;
    vector <int> vec2;

    for (int i = 0; i < 20000000; i++){
        vec.push_back(i);}

    for (int i = 0; i < vec.size(); i++)
    {
        if(vec.at(i) %3 != 0)
            vec2.push_back(i);
    }

    vec = vec2;
    cout << vec.size() << endl;
}
Run Code Online (Sandbox Code Playgroud)

该代码获取 vec 中不能被 3 整除的每个数字并将其复制到 vec2。然后它在 vec 中复制 vec2。它非常快。要处理 20,000,000 个元素,这个算法只需要 0.8 秒!

我用擦除方法做了同样的事情,它需要很多很多时间:

Erase-Version (10k elements)  : 0.04 sec
Erase-Version (100k elements) : 0.6  sec
Erase-Version (1000k elements): 56   sec
Erase-Version (10000k elements): ...still calculating (>30 min)
Run Code Online (Sandbox Code Playgroud)

  • 有趣,但与问题无关! (7认同)
  • 这如何回答问题? (6认同)
  • 那是 std::remove_if (+erase) (4认同)

小智 6

我建议您阅读有关擦除\xe2\x80\x93remove 惯用语的内容
例如:

\n
vec.erase(vec.begin() + 1, vec.begin() + 3);\n
Run Code Online (Sandbox Code Playgroud)\n

这样,您将擦除 的n第一个元素vec,但在擦除第二个元素之前,的所有其他元素vec将被移位,并且向量大小将减少 1。这可能是一个问题,因为您可能会vecsize()为时循环减少

\n

如果您遇到这样的问题,提供的链接建议使用removeremove_if

\n