擦除矢量中的元素使用for循环

时间:2016-02-26 11:30:31

标签: c++ for-loop vector segmentation-fault erase

如何使用for循环通过索引从矢量中擦除元素?我得到一个超出范围错误的向量。我在下面有一个示例代码。

 vector<int> to_erase = {0, 1, 2}; 
 vector<int> data     = {3, 3, 3, 3};

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

    data.erase(data.begin() + to_erase[i]);
 }

我认为这是因为我的向量的大小在每次迭代时都会减少,因此它无法访问索引2.

4 个答案:

答案 0 :(得分:6)

您通常会使用erase–remove idiom来有效地从向量中删除多个元素(逐个删除它们通常效率较低,而且,正如您所见,并非总是微不足道)。最常见的形式是,成语如下:

data.erase(remove_algorithm(begin(data), end(data)), end(data));

在您的情况下,remove_algorithm基于另一个向量中的索引,因此我们也需要提供这些索引:

data.erase(
    remove_indices(begin(data), end(data), begin(to_erase), end(to_erase)),
    end(data));

不幸的是,这样的算法不包含在标准库中。但是,写自己 1

是微不足道的
template <typename It, typename It2>
auto remove_indices(It begin, It end, It2 idx_b, It2 idx_e) -> It {
    using idx_t = typename std::iterator_traits<It2>::value_type;
    std::sort(idx_b, idx_e, std::greater<idx_t>{});

    for (; idx_b != idx_e; ++idx_b) {
        auto pos = begin + *idx_b;
        std::move(std::next(pos), end--, pos);
    }
    return end;
}

在这里,我们首先对要从大到小移除的索引进行排序。接下来,我们循环这些索引。然后,我们(最有效地)将当前位置(要删除)和向量结束之间的所有元素向前移动一个。随后,结尾减少一个(考虑到元素被删除的事实)。

Live code

1 * Ahem *一旦你删除了代码中所有愚蠢的拼写错误。

答案 1 :(得分:0)

在迭代的同时删除元素集合是不安全的,并且是昂贵的。我建议符合条件的每个元素最后都与元素交换。 (最后因为从末尾擦除会更便宜。你可以跟踪你从矢量末尾回来的数量(根据交换次数),并提前打破我们的循环。现在基于您交换了多少元素可以执行以下操作:

data.resize(data.size() - reverse_counter);

int pos = data.size() - reverse_counter;
data.erease(data.begin()+pos, data.end();

只是为了解释这个想法是sudo代码。

如参考文献中所述,最后擦除不会导致重新分配,这是很昂贵的。值得记住的事情: http://www.cplusplus.com/reference/vector/vector/erase/

答案 2 :(得分:0)

  

我认为这是因为我的矢量大小会在每次迭代时减少

是!

你可以通过保留一个额外的变量来做到这一点,该变量计算被删除的元素,如下所示:

#include <iostream>
#include <vector>

using namespace std;

int main() {
        vector<int> to_erase = {0, 1, 2}; 
        vector<int> data     = {3, 3, 3, 3};

        int count_removed = 0;
        for(unsigned int i = 0; i < to_erase.size(); i++)
                data.erase(data.begin() + to_erase[i] - count_removed++);

        for(unsigned int i = 0; i < data.size(); ++i)
                        cout << data[i] << "\n";
        return 0;
}

输出:

  

3

我第一次使用std::erase()时遇到了同样的问题,好问题,+ 1。

答案 3 :(得分:0)

我认为这是一个糟糕的设计,因为您将更改for循环不变量,并且需要大量的解决方法来实现这一点。无论如何,如果你真的想使用for循环,你可以 MAY 标记你要删除的内容并运行一个stl remove_if,如:

#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
using namespace std;

int main() {

    vector<int> to_erase = {0, 1, 2}; 
    vector<int> data     = {3, 3, 3, 3};

    cout << "Before:\n" ;
    for(int i=0; i<data.size(); i++)
        cout << i << "\t";
    cout << endl;   
    for(int i=0; i<data.size(); i++)
        cout << data[i] << "\t";
    cout << endl;

    for(int i = 0; i < to_erase.size(); i++) {
        //data.erase(data.begin() + to_erase[i]);
        data[i] = numeric_limits<int>::max();
    }

    data.erase(remove_if(data.begin(), 
                         data.end(), 
                         [](int i){return i==numeric_limits<int>::max();}), data.end());

    cout << "Next:\n" ;
    for(int i=0; i<data.size(); i++)
        cout << i << "\t";
    cout << endl;
    for(int i=0; i<data.size(); i++)
        cout << data[i] << "\t";

    return 0;
}