2016-02-26 1 views
3

Comment utiliser une boucle for pour effacer des éléments d'un vecteur par son index? Je reçois un vecteur hors de portée erreur. J'ai un exemple de code ci-dessous.Effacer des éléments dans un vecteur en utilisant la boucle

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

Je pense qu'il est parce que la taille de mon vecteur réduit par chaque itération, il ne peut donc pas accéder à l'index 2.

+1

Peut-être que vous pouvez commander le tableau d'index et supprimer les éléments à partir de la dernière – Aleksej

Répondre

5

Vous emploieriez normalement erase–remove idiom supprimer plusieurs éléments d'un vector efficacement (les effacer un à un est généralement moins efficace et, comme vous l'avez vu, pas toujours trivial). Dans sa forme la plus générale, l'idiome ressemble à ceci:

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

Dans votre cas, le remove_algorithm est basé sur les indices dans un autre vecteur, nous avons donc besoin de fournir ceux aussi bien:

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

Malheureusement , un tel algorithme n'est pas contenu dans la bibliothèque standard.Cependant, il est trivial de vous écrire :

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

Ici, nous trions les indices d'abord à retirer du plus grand au plus petit. Ensuite, nous bouclons sur ces indices. Nous déplaçons alors (de façon optimale) tous les éléments entre la position actuelle (à supprimer) et la fin du vecteur de un. Par la suite, la fin est diminuée de un (pour tenir compte du fait qu'un élément a été supprimé).

Live code


* Ahem * Une fois que vous avez supprimé toutes les fautes de frappe stupides dans votre code.

+0

Une solution géniale! –

0

Suppression d'une collection d'éléments quant à lui vous itérer, est dangereux et probalby cher. Je suggérerais que chaque élément qui répond à vos critères soit échangé avec un élément à la fin. (à la fin, car il sera moins cher d'effacer à la fin.) Vous pouvez garder une trace de combien vous revenez de la fin du vecteur (basé sur le nombre de swap), et de rompre tôt le notre de la boucle. combien d'éléments vous permutés vous pouvez faire quelque chose comme:

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

ou

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

Il est code sudo juste pour expliquer l'idée

comme mentionné dans la référence, efface pas au. fin cause ré-allocation, ce qui est cher.Pour garder à l'esprit: http://www.cplusplus.com/reference/vector/vector/erase/

0

Je pense qu'il est parce que la taille de mon vecteur réduit par chaque itération

Oui!

Vous pouvez le faire en gardant une variable supplémentaire, qui compte les éléments qui sont supprimés, comme ceci:

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

Sortie:

J'ai eu la même problème quand j'ai utilisé std::erase(), bonne question, +1.

+1

et bonne réponse! – Marievi

+0

@Marievi espère que RuiQi le trouve utile aussi. – gsamaras

0

Je pense que c'est une mauvaise conception, parce que vous allez changer l'invariant de la boucle for et aurez besoin de beaucoup de solutions pour y arriver. Quoi qu'il en soit, si vous voulez vraiment utiliser une boucle, vous MAI drapeau ce que vous whant à supprimer et exécuter un remove_if stl, quelque chose comme:

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