【问题标题】:Erasing the last element of a vector by looping through it通过循环删除向量的最后一个元素
【发布时间】:2011-03-21 16:49:39
【问题描述】:

我想循环遍历一个向量并擦除与某个条件相对应的某些元素,例如:

vector<int> myvector;
vector<int>::iterator it;

myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);

for(it = myvector.begin(); it != myvector.end(); ++it){

    if((*it) == 4){
        it = myvector.erase(it);
    }
}

现在这工作正常,除非标准像上面的代码那样删除最后一项。你如何避免这种行为?

谢谢。

编辑------------------------------------

现在我循环遍历它的原因是实际上有 4 个向量我需要从中删除元素(但标准只针对一个向量):

这种情况下,这是怎么走的?

vector<int> myvector;
vector<int> myvector2;
vector<int> myvector3;
vector<int> myvector4;
vector<int>::iterator it;
vector<int>::iterator it2;
vector<int>::iterator it3;
vector<int>::iterator it4;

myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);

(假设 myvector2/3/4 里面有值)

it2 = myvector2.begin()
it3 = myvector3.begin()
it4 = myvector4.begin()

for(it = myvector.begin(); it != myvector.end();){

    if((*it) == 4){
        it = myvector.erase(it);
        it2 = myvector2.erase(it2);
        it3 = myvector3.erase(it3);
        it4 = myvector4.erase(it4);
    }
    else{
    ++it;
    ++it2;
    ++it3;
    ++it4;
    }
}

在这种情况下是否对擦除/删除习语进行了修改?

【问题讨论】:

  • "现在这工作正常,除非条件删除最后一项。"不,它没有。每当您擦除元素时,它都有未定义的行为。 (只是想我会提到这一点;其他人已经发布了正确的解决方案。)

标签: c++ vector


【解决方案1】:

通常是删除/擦除习语,看起来像这样:

myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());

编辑:重读您的问题,您提到“某些标准”。如果条件不一定只是删除单个值,您可以使用std::remove_if 而不是std::remove,并在函子中指定您的条件。

Edit2:对于处理四个向量的版本,通常的方法是创建一个包含四个相关值的结构体,并删除整个结构体:

struct x4 { 
    int a, b, c, d;

    // define equality based on the key field:
    bool operator==(x4 const &other) { return a == other.a; }

    x4(int a_, int b_=0, int c_=0, ind d_=0) : a(a_), b(b_), c(c_), d(d_) {}
};

std::vector<x4> myvector;

myvector.erase(std::remove(myvector.begin(), myvector.end(), x4(4));

同样,如果您的标准比您可以在比较运算符中轻松表达的更复杂,您可以使用std::remove_if 而不是std::remove。如果/当您可能需要在不同时间应用不同标准时,这也很有用。

如果您确实需要将数据保存在并行向量中(例如,您将数据提供给需要单独、连续数组的外部数据),那么使用循环可能与替代品。

【讨论】:

  • 不要忘记,如果有重复项,您需要从返回迭代器中擦除到end
【解决方案2】:

不要使用 for 循环执行此操作,已经有一个经过良好调试的算法供您使用。

myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());

【讨论】:

    【解决方案3】:

    我认为你应该把循环写成:

    for(it = myvector.begin(); it != myvector.end(); )
    {
        if((*it) == 4)
            it = myvector.erase(it);
        else
            ++it; //increment here!
    }
    

    因为在您的代码中,如果您找到 4,您会更新 if 块本身中的 it,但之后您再次增加/更新 for 中的 it 也是错误的。这就是为什么我将它移动到 else 块,以确保如果您没有找到 4(或您正在搜索的任何值),it 会增加。

    还要记住erase 返回iterator pointing to the new location of the element that followed the last element erased by the function call

    【讨论】:

    • @Simon:注意 for 循环已经改变。这是因为erase 将返回下一项,而++it 不是您在这种情况下循环时想要执行的操作。
    • @John & Nawaz:谢谢,现在我明白它实际上是在做 ++it 两次并且超出了最后一个元素的范围
    • 我在问题的编辑部分中编写的代码现在有效吗?
    • 您应该避免使用这种结构,而是使用 remove/erase 习惯用法。在最坏的情况下,手动执行擦除可能是 O(N^2) 操作——所有元素都符合删除标准,对于每个元素,所有其他元素都向左移动一个位置,总共 N^2/2 次移动。删除/擦除习语保证为 O(N)
    • @Simon:你的编辑有很多问题。首先,正如您为第一个向量编写的 it = myvector.begin() 一样,您还没有为所有其他向量编写此代码。其次,在第一个向量中找到4,并不意味着您也在其他向量中找到它。第三,增加else 块中的所有迭代器,就好像它们的大小相同。如果一个向量只有较大向量的一半元素怎么办?这将比其他人早达到end,对吗?
    【解决方案4】:

    erase一般和remove一起使用(也可以看看erase-remove成语)如下图

    myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
    

    【讨论】:

    • Prasoon:你把eraseremove 混淆了。 remove 实际上并没有删除元素!
    【解决方案5】:
    for(it = myvector.begin(); it < myvector.end(); ++it){
    
        if((*it) == 4){
            it = myvector.erase(it);
        }
    }
    

    这将确保您的循环在it &gt;= myvector.end() 时中断。

    【讨论】:

    • 如果使用这种循环有任何问题,请帮助我更好地理解这个概念。
    猜你喜欢
    • 1970-01-01
    • 2012-08-20
    • 1970-01-01
    • 2011-04-14
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 2016-12-19
    相关资源
    最近更新 更多