2011-02-03 31 views
6

Utilizzando algoritmi STL (per quanto possibile), come remove_if() e list::erase, c'è un bel modo per rimuovere i duplicati da un elenco definito come segue:Rimuovere i duplicati da una lista <int>

list<int> l;

Si prega di notare che list::unique() funziona solo se la duplicazione si verifica in elementi consecutivi. Nel mio caso, tutti i duplicati devono essere eliminati indipendentemente dalla loro posizione nell'elenco. Inoltre, rimuovere i duplicati significa preservare solo una copia di ciascun elemento nel risultato finale.

MODIFICA: l'opzione per l.sort() seguita da l.unique() non può essere utilizzata poiché distruggerà l'ordine della lista.

+4

Beh, ovviamente si potrebbe chiamare 'l.sort()' prima di chiamare 'l.unique()', ma presumo che ci debba essere una ragione per cui non si può farlo? :) – hrnt

+0

Non sono sicuro degli algoritmi STL, ma il modo ovvio per farlo è quello di scorrere l'elenco di un set di hash: se ciascun elemento non è nel set è univoco quindi aggiungi al set; se è nel set è un duplicato quindi rimuoverlo dalla lista. – Rup

+0

Perché non ci proponi un tuo codice? –

risposta

8

Utilizzando la funzione list::remove_if membro, un set hash temporanea, e l'espressione lambda.

+0

Nota: Questa soluzione evita il trabocchetto che ho notato sulla risposta di José Tomás Tocino avendo 's' essere catturato per riferimento. –

8

Se preservare l'ordine della lista non è importante, si può solo fare list.sort(); list.unique();

Se l'ordine è importante, utilizzare il suggerimento di Rup: ​​

list<int>::iterator iter = l.begin(); 
set<int> elements; 
while (iter != l.end()) { 
    if (elements.find(*iter) != elements.end()) 
    iter = l.erase(iter); 
    else { 
    elements.insert(*iter); 
    ++iter; 
    } 
} 
+2

altrimenti: 'if (elements.insert (* iter) .second) ++ iter else iter = l.erase (iter)'. set :: insert restituisce una coppia di cui il secondo elemento indica se l'inserimento ha avuto esito positivo o non è riuscito a causa di un duplicato. – Benoit

+0

non stai testando il 'estremo()' sulla riga che contiene 'find()'? – Hasturkun

+0

@Hasturkun: si lo ero. Risolto ora :) – hrnt

6

Ha detto che voleva utilizzare l'erase- rimuovere linguaggio, ecco un modo possibile, utilizzando un oggetto funzione:

struct Unifier{ 
    set<int> foundElements; 

    bool operator()(int & a){ 
     if(foundElements.find(a) != foundElements.end()){ 
      return true; 
     }else{ 
      foundElements.insert(a); 
      return false; 
     } 
    } 
}; 


int main(){ 
    list<int> v; 

    v.push_back(5); 
    v.push_back(4); 
    v.push_back(5); 
    v.push_back(3); 
    v.push_back(5); 
    v.push_back(3); 

    copy (v.begin(), v.end(), ostream_iterator<int>(cout," ")); 

    Unifier u; 
    v.remove_if(u); 

    cout << endl << "After:" << endl; 
    copy (v.begin(), v.end(), ostream_iterator<int>(cout," ")); 

} 

Aggiornamento: il codice precedente presenta un bug sottile. Secondo C++ 11 [algorithms.general]/10:

[Nota: se non diversamente specificato, gli algoritmi che accettano oggetti funzione come argomenti sono autorizzati a copiare liberamente tali oggetti funzione. I programmatori per i quali l'identità dell'oggetto è importante dovrebbero prendere in considerazione l'utilizzo di una classe wrapper che punta a un oggetto di implementazione non protetto, ad esempio reference_wrapper<T> (20.8.3) o una soluzione equivalente. Nota -end]

non sembra esserci nessun "altrimenti specificato" per std::list::remove_if, quindi questo codice potrebbe non riuscire a rimuovere tutti i duplicati perché può creare copie del predicato alla partenza, e quindi utilizzare diverse copie del predicato per diverse parti della lista. Example of this actually happening for std::remove_if.

Una semplice correzione per C++ 11 è quello di sostituire v.remove_if(u) con:

v.remove_if(reference_wrapper<decltype(u)>(u)); 

In C++ 03 non sono sicuro se la citazione di cui sopra era presente; ma se fosse una correzione, sarebbe necessario rendere statico lo foundElements o il refactor Unifier in modo che tutte le copie di esso facciano riferimento a una singola istanza di foundElements.

Link to related question

+1

Ulteriori informazioni: http://en.wikipedia.org/wiki/Erase-remove_idiom –

+1

Perché si prende un parametro per riferimento? –

+1

Non è necessario usare l'erase-remove idiom con std :: list. Potresti semplicemente chiamare v.remove_if (u); Inoltre, i foundElements non devono essere statici. – hrnt

Problemi correlati