¿Cómo elimino un elemento de un vector stl con un cierto valor?

145

Estaba mirando la documentación de la API para stl vector, y noté que no había ningún método en la clase de vector que permitiera la eliminación de un elemento con un cierto valor. Esto parece una operación común, y parece extraño que no haya una forma integrada de hacer esto.

Bradtgmurray
fuente
2
Sé que he mencionado esto varias veces antes, pero el libro de Scott Meyer, Effective STL, cubre estas trampas de una manera clara.
Rob Wells
1
Relacionado: stackoverflow.com/questions/3385229/…
bobobobo
Esta podría ser una lectura interesante para usted: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

Respuestas:

165

std::removeen realidad no borra el elemento del contenedor, pero devuelve el nuevo iterador final al que se puede pasar container_type::erasepara hacer la eliminación REAL de los elementos adicionales que ahora están al final del contenedor:

std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());
Jim Buck
fuente
3
¿Esta forma de declaración todo en uno depende del orden en el que el compilador evalúa los argumentos, o se vec.end()garantiza que sea el mismo en ambos lados de la llamada std::remove? Me parece que leer otras partes de la web es seguro, pero debe indicarse claramente.
dmckee --- ex-gatito moderador
1
Está bien. El resultado de vec.end()no necesita ser el mismo; solo necesita ser correcto (que es).
Jim Buck el
8
vec.end()necesita ser lo mismo, pero está bien porque std::removeno lo cambia. Si lo cambiara (e invalidara el valor anterior), entonces habría un problema: el orden de evaluación de los parámetros no está especificado y, por lo tanto, no sabría si el segundo vec.end()todavía es válido en el momento en que se usa. La razón por la que es igual es simple, std::removeno cambia el tamaño del contenedor, solo mueve el contenido.
Steve Jessop
2
He encontrado esta pregunta importante, ya que tengo el mismo problema. Pero, mi estudio visual toma std::removesolo un argumento; eso es const char *_Filename. ¿Qué método necesito llamar?
Victor
15
Esa es la versión de removeque elimina un archivo. Debe incluir <algorithm>para acceder a la versión de removeque trata con contenedores.
Jim Buck el
64

Si desea eliminar un elemento, lo siguiente será un poco más eficiente.

std::vector<int> v;


auto it = std::find(v.begin(), v.end(), 5);
if(it != v.end())
    v.erase(it);

o puede evitar los gastos generales de mover los artículos si el pedido no le importa:

std::vector<int> v;

auto it = std::find(v.begin(), v.end(), 5);

if (it != v.end()) {
  using std::swap;

  // swap the one to be removed with the last element
  // and remove the item at the end of the container
  // to prevent moving all items after '5' by one
  swap(*it, v.back());
  v.pop_back();
}
Etherealone
fuente
3
Tenga en cuenta que esto no eliminará duplicados del elemento si existen, mientras que el enfoque std :: remove_if sí.
Den-Jason el
15

Use el método global std :: remove con el iterador de inicio y fin, y luego use std :: vector.erase para eliminar realmente los elementos.

Enlaces de documentación
std :: remove http://www.cppreference.com/cppalgorithm/remove.html
std :: vector.erase http://www.cppreference.com/cppvector/erase.html

std::vector<int> v;
v.push_back(1);
v.push_back(2);

//Vector should contain the elements 1, 2

//Find new end iterator
std::vector<int>::iterator newEnd = std::remove(v.begin(), v.end(), 1);

//Erase the "removed" elements.
v.erase(newEnd, v.end());

//Vector should now only contain 2

Gracias a Jim Buck por señalar mi error.

Bradtgmurray
fuente
Esto tiene la capacidad de evitar el movimiento de otros elementos al borrar un elemento en el medio del vector, por lo tanto, esto es más rápido. Primero los mueve al final del vector y luego puede descartarlos desde el final del vector.
Etherealone
5

Las otras respuestas cubren cómo hacerlo bien, pero pensé que también señalaría que no es realmente extraño que esto no esté en la API de vectores: es ineficiente, la búsqueda lineal a través del vector para el valor, seguido de un montón de copiar para eliminarlo.

Si está haciendo esta operación intensivamente, puede valer la pena considerar std :: set en su lugar por este motivo.

Luke Halliwell
fuente
5

Si tiene un vector sin clasificar, simplemente puede intercambiar con el último elemento del vector y luego resize() .

Con un recipiente ordenada, podrás mejor con std::vector::erase(). Tenga en cuenta que hay un std::remove()definido en <algorithm>, pero que en realidad no hace el borrado. (Lea la documentación detenidamente).

nsanders
fuente
3

De c ++ 20 :

Se introdujo una función no miembro std::erase, que toma el vector y el valor para eliminarlos como entradas.

ex:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);
Pavan Chandaka
fuente
2
¡No solo eso, está sobrecargado para cada contenedor específico!
HolyBlackCat
... y aprovecha las propiedades específicas del contenedor, como map::erase!
LF
2

Vea también std :: remove_if para poder usar un predicado ...

Aquí está el ejemplo del enlace de arriba:

vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(2);
V.push_back(8);
V.push_back(5);
V.push_back(7);

copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
    // The output is "1 4 2 8 5 7"

vector<int>::iterator new_end = 
    remove_if(V.begin(), V.end(), 
              compose1(bind2nd(equal_to<int>(), 0),
                       bind2nd(modulus<int>(), 2)));
V.erase(new_end, V.end()); [1]

copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
    // The output is "1 5 7".
Xavier Nodet
fuente
2
Agrega más detalles a esta publicación. Tal como está, la mayoría de su contenido proviene de un enlace y se perdería si el enlace se rompe.
Mick MacCallum
¿Qué pasa con el hecho de que bind2nd es - (en desuso en C ++ 11) (eliminado en C ++ 17)
Idan Banani
0

Si quieres hacerlo sin ningún extra incluye:

vector<IComponent*> myComponents; //assume it has items in it already.
void RemoveComponent(IComponent* componentToRemove)
{
    IComponent* juggler;

    if (componentToRemove != NULL)
    {
        for (int currComponentIndex = 0; currComponentIndex < myComponents.size(); currComponentIndex++)
        {
            if (componentToRemove == myComponents[currComponentIndex])
            {
                //Since we don't care about order, swap with the last element, then delete it.
                juggler = myComponents[currComponentIndex];
                myComponents[currComponentIndex] = myComponents[myComponents.size() - 1];
                myComponents[myComponents.size() - 1] = juggler;

                //Remove it from memory and let the vector know too.
                myComponents.pop_back();
                delete juggler;
            }
        }
    }
}
Katianie
fuente
0

Hay dos formas en que puede usar para borrar un elemento en particular. tomemos un vector

std :: vector < int > v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(40);
v.push_back(50);

1) Forma no eficiente: aunque parece ser bastante eficiente, no es porque la función de borrado elimina los elementos y desplaza todos los elementos hacia la izquierda en 1. por lo que su complejidad será O (n ^ 2)

std :: vector < int > :: iterator itr = v.begin();
int value = 40;
while ( itr != v.end() )
{
   if(*itr == value)
   { 
      v.erase(itr);
   }
   else
       ++itr;
}

2) Manera eficiente (RECOMENDADO) : También se conoce como ERASE - REMOVE idioms .

  • std :: remove transforma el rango dado en un rango con todos los elementos que se comparan que no son iguales al elemento dado desplazado al inicio del contenedor.
  • Por lo tanto, en realidad no elimine los elementos coincidentes. Simplemente cambió el no coincidente al inicio y le da un iterador al nuevo final válido. Solo requiere O (n) complejidad.

La salida del algoritmo de eliminación es:

10 20 30 50 40 50 

como tipo de retorno de eliminar es iterador al nuevo final de ese rango.

template <class ForwardIterator, class T>
  ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);

Ahora use la función de borrado del vector para eliminar elementos del nuevo extremo al extremo anterior del vector. Requiere O (1) tiempo.

v.erase ( std :: remove (v.begin() , v.end() , element ) , v.end () );

entonces este método funciona en O (n)

Praveen Kumar
fuente
0

* *

La comunidad de C ++ ha escuchado tu solicitud :)

* *

C ++ 20 proporciona una manera fácil de hacerlo ahora. Se pone tan simple como:

#include <vector>
...
vector<int> cnt{5, 0, 2, 8, 0, 7};
std::erase(cnt, 0);

Deberías consultar std :: erase y std :: erase_if .

No solo eliminará todos los elementos del valor (aquí '0'), lo hará en O (n) complejidad de tiempo. Que es lo mejor que puedes conseguir.

Si su compilador no es compatible con C ++ 20, debe usar el lenguaje erase-remove :

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
Harshad Sharma
fuente