Comment effacer un élément de std :: vector <> par index?


508

J'ai un std :: vector <int>, et je veux supprimer le nième élément. Comment je fais ça?

std::vector<int> vec;

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

vec.erase(???);

4
Pensez à utiliser un std :: deque qui fournit l'insertion et la suppression aux deux extrémités.
Dario

40
Non, ne pensez pas à utiliser deque simplement parce que vous voudrez peut-être supprimer un élément, c'est vraiment un mauvais conseil. Il y a toute une série de raisons pour lesquelles vous pouvez utiliser deque ou vector. Il est vrai que supprimer un élément d'un vecteur peut être coûteux - surtout si le vecteur est grand, mais il n'y a aucune raison de penser qu'un deque serait mieux qu'un vecteur de l'exemple de code que vous venez de publier.
Chouette du

6
Par exemple, si vous avez une application graphique où vous affichez une "liste" de choses où vous insérez / supprimez des choses de manière interactive, considérez que vous parcourez la liste 50-100 fois par seconde pour les afficher, et vous ajoutez / supprimez des choses quelques-unes fois par minute. La mise en œuvre de la "liste" comme vecteur est donc probablement une meilleure option en termes d'efficacité totale.
Michel Billaud

Réponses:


706

Pour supprimer un seul élément, vous pouvez faire:

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

Ou, pour supprimer plusieurs éléments à la fois:

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

50
Notez également que le binaire operator+n'est pas nécessairement défini pour les itérateurs sur d'autres types de conteneurs, comme list<T>::iterator(vous ne pouvez pas le faire list.begin() + 2sur un std::list, vous devez l'utiliser std::advancepour cela)
bobobobo

déclarez-vous que le "+1" est le premier élément myVector [0] ou la position réelle myVector [1]
K - La toxicité dans le SO augmente.

2
Avec avance, vous devez enregistrer l'itérateur dans une variable. Si vous utilisez std :: next, vous pouvez le faire sur une seule ligne: vec.erase (next (begin (vec), 123));
dani

8
Merci à tous ceux qui ont répondu. Que penser d'une conception de classe lorsqu'une opération aussi simple que la suppression d'un élément nécessite que l'on vienne sur StackOverflow?
Pierre

5
@Pierre parce que l' indice numérique d'un élément particulier n'est pas le modèle principal d'accès, l' itérateur l' est. Toutes les fonctions qui regardent les éléments d'un conteneur utilisent les itérateurs de ce conteneur. Par exemplestd::find_if
Caleth

212

La méthode d'effacement sur std :: vector est surchargée, il est donc probablement plus clair d'appeler

vec.erase(vec.begin() + index);

lorsque vous ne souhaitez effacer qu'un seul élément.


3
Mais ce problème apparaît quel que soit le nombre d'éléments dont vous disposez.
Zyx 2000

15
s'il n'y a qu'un seul élément, l'index est 0, et donc vous obtenez vec.begin()ce qui est valide.
Anne Quinn

28
Je souhaite que quelqu'un aurait mentionné que vec.erase(0)cela ne fonctionne pas, mais vec.erase(vec.begin()+0)(ou sans +0) le fait. Sinon, je ne reçois aucun appel de fonction correspondant, c'est pourquoi je suis venu ici
qrtLs

@qrtLs vec.erase(0)peut en fait se compiler s'il 0arrive d'être interprété comme la constante du pointeur nul ...
LF

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

2
Max, ce qui rend cette fonction meilleure que: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }je ne dis pas que c'est mieux, simplement en demandant par intérêt personnel et en retournant le meilleur résultat que cette question pourrait obtenir.

13
@JoeyvG: Puisque a vector<T>::iteratorest un itérateur à accès aléatoire, votre version est correcte et peut-être un peu plus claire. Mais la version publiée par Max devrait fonctionner très bien si vous changez le conteneur en un autre qui ne prend pas en charge les itérateurs à accès aléatoire
Lily Ballard

2
C'est imo la meilleure réponse, car elle s'applique également à d'autres formats de conteneurs. Vous pouvez également utiliser std :: next ().
Bim

Une bien meilleure approche car elle ne repose pas sur les éléments internes du conteneur.
BartoszKP

std :: Advance n'est nécessaire que si vous pensez que ce ne sera pas un vecteur, c'est-à-dire une liste. Mais comme vous l'avez spécifié ici, l'opérateur + ne serait-il pas plus simple? Selon ce stackoverflow.com/questions/1668088/… il y a un gain de performance possible avec l'opérateur +
Neil McGill

14

La eraseméthode sera utilisée de deux manières:

  1. Effacement d'un seul élément:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. Effacement d'une gamme d'éléments:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element

7
Il s'agit d'une réponse en double près de 7 ans après la réponse acceptée. Veuillez ne pas faire ça.
AlastairG

10

En fait, la erasefonction fonctionne pour deux profils:

  • Suppression d'un seul élément

    iterator erase (iterator position);
  • Suppression d'une gamme d'éléments

    iterator erase (iterator first, iterator last);

Puisque std :: vec.begin () marque le début du conteneur et si nous voulons supprimer le ième élément de notre vecteur, nous pouvons utiliser:

vec.erase(vec.begin() + index);

Si vous regardez attentivement, vec.begin () n'est qu'un pointeur vers la position de départ de notre vecteur et l'ajout de la valeur de i augmente le pointeur à la position i, nous pouvons donc accéder au pointeur vers le ième élément en:

&vec[i]

On peut donc écrire:

vec.erase(&vec[i]); // To delete the ith element

6
-1 La dernière ligne ne se compile pas (au moins dans VS2017). Le code suppose que vector :: iterator est implicitement constructible à partir d'un pointeur brut, ce qui n'est pas requis par la norme.
CuriousGeorge

1
Cela est particulièrement vrai pour les itérateurs de débogage
Nishant Singh

9

Si vous avez un vecteur non ordonné, vous pouvez profiter du fait qu'il n'est pas ordonné et utiliser quelque chose que j'ai vu de Dan Higgins au CPPCON

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

Étant donné que l'ordre des listes n'a pas d'importance, prenez simplement le dernier élément de la liste et copiez-le par-dessus l'élément que vous souhaitez supprimer, puis sautez et supprimez le dernier élément.


Je pense que c'est la meilleure réponse si le vecteur n'est pas ordonné. Il ne repose pas sur l'hypothèse qui iterator + indexvous donnera en fait la position de l'itérateur à cet index, ce qui n'est pas le cas de tous les conteneurs itérables. C'est également une complexité constante au lieu d'être linéaire en tirant parti du pointeur arrière.
theferrit32

1
Cela doit totalement être ajouté à la bibliothèque standard au fur unordered_removeet à mesure unordered_remove_ifque cela n'a pas été le cas et que je l'ai manqué, ce qui se produit de plus en plus ces jours-ci :)
Will Crawford

Si suggérerait d'utiliser l'affectation de déplacement ou l'échange au lieu d'une affectation de copie.
Carsten S

std::removeréorganise le conteneur pour que tous les éléments à supprimer soient à la fin, pas besoin de le faire manuellement comme ceci si vous utilisez C ++ 17.
keith

@keith comment ça std::removeaide? cppreference affirme que même en C ++ 17, toutes les removesurcharges nécessitent un prédicat et aucune ne prend d'index.
Paul Du Bois

4

Si vous travaillez avec de grands vecteurs (taille> 100 000) et que vous souhaitez supprimer de nombreux éléments, je recommanderais de faire quelque chose comme ceci:

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

Le code prend chaque nombre dans vec qui ne peut pas être divisé par 3 et le copie dans vec2. Ensuite, il copie vec2 dans vec. C'est assez rapide. Pour traiter 20 000 000 éléments, cet algorithme ne prend que 0,8 s!

J'ai fait la même chose avec la méthode d'effacement, et cela prend beaucoup de temps:

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)

6
comment cela répond-il à la question?
Regis Portalez

4
Intéressant, mais sans rapport avec la question!
Roddy

Un algorithme sur place ne sera-t-il pas plus rapide?
user202729

2
c'est std :: remove_if (+ effacer)
RiaD


3

Je suggère de lire ceci car je crois que c'est ce que vous recherchez.https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Si vous utilisez par exemple

 vec.erase(vec.begin() + 1, vec.begin() + 3);

vous effacerez le nième élément du vecteur mais lorsque vous effacerez le deuxième élément, tous les autres éléments du vecteur seront décalés et la taille du vecteur sera -1. Cela peut être un problème si vous parcourez le vecteur car la taille du vecteur () diminue. Si vous avez un problème comme celui-ci, le lien proposé suggère d'utiliser l'algorithme existant dans la bibliothèque C ++ standard. et "supprimer" ou "supprimer_si".

J'espère que cela a aidé


0

Les réponses précédentes supposent que vous avez toujours un index signé. Malheureusement, les std::vectorutilisations size_typepour l'indexation, etdifference_type pour l'arithmétique des itérateurs, donc ils ne fonctionnent pas ensemble si "-Wconversion" et amis sont activés. C'est une autre façon de répondre à la question, tout en étant capable de gérer à la fois signé et non signé:

Ôter:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type>
void remove(std::vector<T> &v, I index)
{
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index);
    v.erase(iter);
}

Prendre:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type>
T take(std::vector<T> &v, I index)
{
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index);

    auto val = *iter;
    v.erase(iter);

    return val;
}

0

voici une autre façon de le faire si vous voulez supprimer un élément en le trouvant avec sa valeur dans vector, il vous suffit de le faire sur vector.

vector<int> ar(n);
ar.erase(remove(ar.begin(), ar.end()), (place your value here from vector array));

cela supprimera votre valeur d'ici. Merci


0

Que dis-tu de ça?

void squeeze(vector<int> &v)
{
    int j = 0;
    for (int i = 1; i < v.size(); i++)
        if (v[i] != v[j] && ++j != i)
            v[j] = v[i];
    v.resize(j + 1);
}

-4

le moyen le plus rapide (pour programmer des concours par complexité temporelle () = constante)

peut effacer 100M d'élément en 1 seconde;

    vector<int> it = (vector<int>::iterator) &vec[pos];
    vec.erase(it);

et le plus lisible: vec.erase(vec.begin() + pos);


2
C'est très non portable; cela fonctionnera avec libstdc ++, mais pas libc ++ et pas avec MSVC. vector<int>::iteratorn'est pas nécessairement le même queint *
Marshall Clow

2
C'est dégoûtant, je pense que je vais changer libstdc ++ pour l'empêcher de fonctionner.
Jonathan Wakely
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.