Borrar elementos de un vector

101

Quiero borrar un elemento de un vector usando el método de borrado. Pero el problema aquí es que no se garantiza que el elemento ocurra solo una vez en el vector. Puede estar presente varias veces y necesito borrarlas todas. Mi código es algo como esto:

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

Este código obviamente falla porque estoy cambiando el final del vector mientras lo itero. ¿Cuál es la mejor manera de lograr esto? Es decir, ¿hay alguna forma de hacer esto sin iterar a través del vector varias veces o crear una copia más del vector?

Naveen
fuente

Respuestas:

167

Usa el modismo eliminar / borrar :

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

Lo que sucede es que removecompacta los elementos que difieren del valor a eliminar ( number_in) al comienzo del vectory devuelve el iterador al primer elemento después de ese rango. Luego eraseelimina estos elementos (cuyo valor no está especificado).

Motti
fuente
2
std::remove()desplaza elementos de modo que se sobrescriban los elementos a eliminar. El algoritmo no cambia el tamaño del contenedor, y si nse eliminan elementos, no está definido cuáles son los últimos nelementos.
wilhelmtell
20
El lenguaje borrar-eliminar se describe en el artículo 32 del libro "STL eficaz: 50 formas específicas de mejorar el uso de la biblioteca de plantillas estándar" de Scott Meyers.
Alessandro Jacopson
1
@Benjin no se necesita ninguna actualización, llamará a los destructores de los objetos si existen.
Motti
54
Los 'modismos' de STL como este me hacen usar Python para proyectos pequeños.
Johannes Overmann
1
@LouisDionne que se refiere a la sobrecarga de un iterador, estoy usando la sobrecarga de dos iteradores
Motti
53

Llamar a borrar invalidará los iteradores, puede usar:

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

}

O puede usar std :: remove_if junto con un functor y 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());

En lugar de escribir su propio functor en este caso, podría usar std :: remove :

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

En C ++ 11, podría usar una lambda en lugar de un functor:

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), [number_in](int number){ return number == number_in; }), myNumbers.end());

En C ++ 17 std :: experimental :: erase y std :: experimental :: erase_if también están disponibles, en C ++ 20 estos son (finalmente) renombrados a std :: erase y std :: erase_if :

std::vector<int> myNumbers;
std::erase_if(myNumbers, Eraser(number_in)); // or use lambda

o:

std::vector<int> myNumbers;
std::erase(myNumbers, number_in);
dalle
fuente
2
¿Por qué usar su propio functor cuando puede usar equal_to? :-P sgi.com/tech/stl/equal_to.html
Chris Jester-Young
3
Por cierto, llamar erasecon removees la forma canónica de hacer esto.
Konrad Rudolph
1
Creo que hace exactamente eso. pero debería usar remove_if si usa su propio functor iirc. o simplemente use eliminar sin el functor
Johannes Schaub - litb
3
+1 El código deletreado simplemente me ayudó en una competencia de programación, mientras que "simplemente use el idioma de eliminar-borrar" no lo hizo.
13
  1. Puede iterar utilizando el acceso al índice,

  2. Para evitar la complejidad O (n ^ 2), puede utilizar dos índices, i - índice de prueba actual, j - índice para almacenar el siguiente elemento y al final del ciclo un nuevo tamaño del vector.

código:

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

En tal caso, no puede invalidar los iteradores, la complejidad es O (n) y el código es muy conciso y no es necesario escribir algunas clases auxiliares, aunque en algunos casos el uso de clases auxiliares puede beneficiarse de un código más flexible.

Este código no usa erasemétodo, pero resuelve su tarea.

Usando stl puro, puede hacer esto de la siguiente manera (esto es similar a la respuesta 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());
}
sergtk
fuente
4

Dependiendo de por qué está haciendo esto, usar un std :: set podría ser una mejor idea que std :: vector.

Permite que cada elemento ocurra solo una vez. Si lo agrega varias veces, solo habrá una instancia para borrar de todos modos. Esto hará que la operación de borrado sea trivial. La operación de borrado también tendrá una complejidad de tiempo menor que en el vector, sin embargo, agregar elementos es más lento en el conjunto, por lo que podría no ser una gran ventaja.

Por supuesto, esto no funcionará si está interesado en la cantidad de veces que se agregó un elemento a su vector o el orden en que se agregaron los elementos.

Laserallan
fuente
-1

Para borrar el primer elemento puede utilizar:

vector<int> mV{ 1, 2, 3, 4, 5 }; 
vector<int>::iterator it; 

it = mV.begin(); 
mV.erase(it); 
Amit Vujic
fuente
1
Ésa no era la cuestión. ¡Tu respuesta de 11 años después no parece relevante, Amit!
Asteroides con alas