Come rimuovo un oggetto da un vettore stl con un certo valore?


145

Stavo guardando la documentazione dell'API per stl vector e ho notato che non esisteva alcun metodo sulla classe vector che consentisse la rimozione di un elemento con un certo valore. Sembra un'operazione comune e sembra strano che non ci sia un modo per farlo.


2
So di averlo menzionato diverse volte prima, ma il libro di Scott Meyer, Effective STL, tratta in modo chiaro questi gotcha.
Rob Wells,


Questa potrebbe essere una lettura interessante per te: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

Risposte:


165

std::removein realtà non cancella l'elemento dal contenitore, ma restituisce il nuovo iteratore di fine che può essere passato container_type::eraseper fare la rimozione REALE degli elementi extra che sono ora alla fine del contenitore:

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

3
Questo modulo di istruzioni all-in-one dipende dall'ordine in cui il compilatore valuta gli argomenti o è vec.end()garantito che sia lo stesso su entrambi i lati della chiamata std::remove? Mi sembra che leggere altre parti del web in questo modo sia sicuro, ma dovrebbe essere indicato chiaramente.
dmckee --- ex gattino moderatore

1
Va bene. Il risultato di vec.end()non deve essere lo stesso; deve solo essere corretto (quale è).
Jim Buck,

8
vec.end()deve essere lo stesso, ma va bene perché std::removenon lo cambia. Se lo cambiasse (e invalidasse il vecchio valore) allora ci sarebbe un problema: l'ordine di valutazione dei parametri non è specificato e quindi non sapresti se il secondo vec.end()è ancora valido al momento dell'uso. Il motivo per cui è lo stesso è semplice, std::removenon cambia la dimensione del contenitore, ma sposta semplicemente il contenuto.
Steve Jessop,

2
Ho trovato questa domanda importante, dato che ho lo stesso problema. Ma il mio studio visivo prende std::removesolo una discussione; quello è const char *_Filename. Quale metodo devo chiamare?
Victor,

15
Questa è la versione removeche elimina un file. È necessario includere <algorithm>per accedere alla versione relativa removeai contenitori.
Jim Buck,

64

Se si desidera rimuovere un elemento, quanto segue sarà un po 'più efficiente.

std::vector<int> v;


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

oppure potresti evitare il sovraccarico di spostare gli articoli se l'ordine non ti interessa:

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

3
Nota che questo non rimuoverà i duplicati dell'elemento se esistono, mentre l'approccio std :: remove_if lo fa.
Den-Jason,

15

Utilizzare il metodo globale std :: remove con iteratore iniziale e finale, quindi utilizzare std :: vector.erase per rimuovere effettivamente gli elementi.

Link alla documentazione
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

Grazie a Jim Buck per aver segnalato il mio errore.


Ciò ha la capacità di impedire il movimento di altri elementi alla cancellazione di un elemento nel mezzo del vettore, quindi questo è più veloce. Li sposta prima alla fine del vettore, quindi puoi semplicemente scartarli dalla fine del vettore.
Etherealone

5

Le altre risposte spiegano come farlo bene, ma ho anche pensato di sottolineare che non è davvero strano che questo non sia nell'API vettoriale: è inefficiente, la ricerca lineare nel vettore per il valore, seguita da un gruppo di copia per rimuoverlo.

Se stai facendo questa operazione intensamente, può valere la pena considerare invece std :: set per questo motivo.


5

Se hai un vettore non ordinato, allora puoi semplicemente scambiare con l'ultimo elemento vettoriale resize().

Con un contenitore ordinata, sarete meglio via con std::vector::erase(). Si noti che esiste un std::remove()definito in <algorithm>, ma che in realtà non esegue la cancellazione. (Leggi attentamente la documentazione).



3

Da c ++ 20 :

Introduzione di una funzione non membro std::erase, che accetta la rimozione del vettore e del valore come input.

ex:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);

2
Non solo, è sovraccarico per ogni contenitore specifico!
HolyBlackCat

... e sfrutta le proprietà specifiche del contenitore, come map::erase!
LF

2

Vedi anche std :: remove_if per poter usare un predicato ...

Ecco l'esempio dal link sopra:

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".

2
Aggiungi maggiori dettagli a questo post. Allo stato attuale, la maggior parte dei suoi contenuti proviene da un collegamento e andrebbe persa se il collegamento dovesse interrompersi.
Mick MacCallum,

che dire del fatto bind2nd è - (deprecato in C ++ 11) (rimosso in C ++ 17)
Idan Banani il

0

Se vuoi farlo senza alcun extra include:

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

0

Esistono due modi in cui è possibile utilizzare per cancellare un elemento in particolare. prendiamo un vettore

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) Modo non efficiente: anche se sembra essere abbastanza efficiente ma non è perché la funzione di cancellazione cancella gli elementi e sposta tutti gli elementi verso sinistra di 1. quindi la sua complessità sarà 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) Modo efficiente (CONSIGLIATO) : è anche noto come ERASE - REMOVE idioms .

  • std :: remove trasforma l'intervallo dato in un intervallo con tutti gli elementi che si confrontano non uguali a un dato elemento spostato all'inizio del contenitore.
  • Quindi, in realtà non rimuovere gli elementi corrispondenti. Ha appena spostato il non abbinato all'inizio e dà un iteratore alla nuova fine valida. Richiede solo O (n) complessità.

l'output dell'algoritmo remove è:

10 20 30 50 40 50 

come tipo di ritorno di rimozione è iteratore alla nuova fine di quell'intervallo.

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

Ora usa la funzione di cancellazione del vettore per eliminare elementi dalla nuova estremità alla vecchia estremità del vettore. Richiede O (1) tempo.

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

quindi questo metodo funziona in O (n)


0

*

La community C ++ ha ascoltato la tua richiesta :)

*

C ++ 20 offre un modo semplice per farlo ora. Diventa semplice come:

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

Dovresti dare un'occhiata a std :: erase e std :: erase_if .

Non solo rimuoverà tutti gli elementi del valore (qui '0'), ma lo farà anche in O (n) complessità temporale . Qual è il meglio che puoi ottenere.

Se il compilatore non supporta C ++ 20, è necessario utilizzare il linguaggio erase-remove :

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.