2016-02-26 4 views
3

Как использовать цикл 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.

+1

Возможно, вы можете заказать массив индексов и удалить элементы, начиная с последнего – Aleksej

ответ

5

Вы обычно используете 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)); 

К сожалению , такой алгоритм не содержится в стандартной библиотеке.Тем не менее, это тривиально, чтобы написать :

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


* гм * После того, как вы удалили все глупые опечатки в вашем коде.

+0

Удивительное решение! –

0

Удаления коллекции элементов между тем вы итерацией, небезопасен и probalby дорого. Я бы предположил, что каждый элемент, соответствующий вашим критериям, заменяется элементом в конце. (в конце, потому что будет дешевле стереть с конца. Вы можете следить за тем, сколько вы вернулись из конца вектора (в зависимости от количества свопов) и разорвать ранний цикл. сколько элементов вы поменялись местами вы можете сделать что-то вроде:

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/

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; 
} 

Выход:

Я имел такой же проблема когда я впервые использовал std::erase(), хороший вопрос, +1.

+1

и получить хороший ответ! – Marievi

+0

@ Марьеви надеется, что RuiQi тоже будет полезен. – gsamaras

0

Я думаю, что это плохой дизайн, потому что вы измените инвариант цикла и потребуете много обходного пути, чтобы это произошло. Во всяком случае, если вы действительно хотите использовать для петли, вы МАЙ флага, что вы whant удалить и запустить СТЛ 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; 
} 
Смежные вопросы