web-dev-qa-db-fra.com

Effacer des éléments d'un vecteur

Je veux effacer un élément d'un vecteur en utilisant la méthode d'effacement. Mais le problème ici est qu’il n’est pas garanti que l’élément n’apparaisse qu’une fois dans le vecteur. Il peut être présent plusieurs fois et je dois les effacer toutes. Mon code ressemble à ceci:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    std::vector<int>::iterator endIter = myNumbers_in.end();
    for(; iter != endIter; ++iter)
    {
        if(*iter == number_in)
        {
            myNumbers_in.erase(iter);
        }
    }
}

int main(int argc, char* argv[])
{
    std::vector<int> myNmbers;
    for(int i = 0; i < 2; ++i)
    {
        myNmbers.Push_back(i);
        myNmbers.Push_back(i);
    }

    erase(myNmbers, 1);

    return 0;
}

Ce code se bloque évidemment parce que je change la fin du vecteur en le parcourant. Quel est le meilleur moyen d'y parvenir? C'est à dire. y a-t-il moyen de le faire sans parcourir le vecteur plusieurs fois ni créer une copie supplémentaire du vecteur?

91
Naveen

Utilisez le enlever/effacer l'idiome :

std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());

Qu'est-ce qui se passe est que remove compacte les éléments qui diffèrent de la valeur à supprimer (number_in) au début du vector et renvoie l'itérateur au premier élément après cette plage. Ensuite, erase supprime ces éléments (la valeur de qui n'est pas spécifiée).

148
Motti

Appeler effacer va invalider les itérateurs, vous pouvez utiliser:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    while (iter != myNumbers_in.end())
    {
        if (*iter == number_in)
        {
            iter = myNumbers_in.erase(iter);
        }
        else
        {
           ++iter;
        }
    }

}

Ou vous pouvez utiliser std :: remove_if avec un foncteur et std :: vector :: erase:

struct Eraser
{
    Eraser(int number_in) : number_in(number_in) {}
    int number_in;
    bool operator()(int i) const
    {
        return i == number_in;
    }
};

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());

Au lieu d'écrire votre propre foncteur dans ce cas, vous pouvez utiliser std :: remove :

std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());
47
dalle
  1. Vous pouvez itérer en utilisant l'accès à l'index,

  2. Pour éviter la complexité de O (n ^ 2), vous pouvez utiliser deux index, i - index de test en cours, j - index pour stocker le prochain élément et à la fin du cycle la nouvelle taille du vecteur.

code:

void erase(std::vector<int>& v, int num)
{
  size_t j = 0;
  for (size_t i = 0; i < v.size(); ++i) {
    if (v[i] != num) v[j++] = v[i];
  }
  // trim vector to new size
  v.resize(j);
}

Dans ce cas, vous n'avez pas d'invalidation d'itérateurs, la complexité est O (n) et le code est très concis et vous n'avez pas besoin d'écrire des classes d'assistance, bien que, dans certains cas, l'utilisation de classes d'assistance puisse bénéficier d'un code plus souple.

Ce code n'utilise pas la méthode erase, mais résout votre tâche.

En utilisant pure stl, vous pouvez le faire de la manière suivante (cela ressemble à la réponse de Motti):

#include <algorithm>

void erase(std::vector<int>& v, int num) {
    vector<int>::iterator it = remove(v.begin(), v.end(), num);
    v.erase(it, v.end());
}
12
sergtk

Selon la raison, utiliser un std :: set pourrait être une meilleure idée que std :: vector.

Il permet à chaque élément de se produire qu'une seule fois. Si vous l'ajoutez plusieurs fois, il ne restera de toute façon qu'une seule instance à effacer. Cela rendra l'opération d'effacement triviale. L’opération d’effacement aura également une complexité temporelle inférieure à celle du vecteur; toutefois, l’ajout d’éléments est plus lent sur l’ensemble, de sorte que cela n’est peut-être pas un avantage.

Bien entendu, cela ne fonctionnera pas si vous souhaitez savoir combien de fois un élément a été ajouté à votre vecteur ou dans quel ordre les éléments ont été ajoutés.

3
Laserallan