2014-05-21 10 views
6

C'è un modo migliore (più veloce o con meno simboli di codice) di cancellare l'elemento e riaggiungerlo sul retro?Spostare un elemento vettoriale sul retro del vettore

template <typename T> 
void moveItemToBack(std::vector<T>& v, size_t itemIndex) 
{ 
    T tmp(v[itemIndex]); 
    v.erase(v.begin() + itemIndex); 
    v.push_back(tmp); 
} 
+0

Non proprio ... Il vettore non è un modo molto efficiente per archiviare le cose che devono essere rimosse dall'inizio. Guardate 'std :: queue' o' std :: dequeue' – IdeaHat

+0

@MadScienceDreams: std :: dequeue non è migliore qui, e ho bisogno di accesso casuale. –

+0

Quindi la struttura efficiente da utilizzare è creare il proprio buffer circolare. – IdeaHat

risposta

27

È possibile eseguire questa operazione con std::rotate dalla libreria standard. Poiché ciò non modifica la dimensione del vettore, non innescherà anche una riallocazione. La funzione sarebbe simile a questa:

template <typename T> 
void moveItemToBack(std::vector<T>& v, size_t itemIndex) 
{ 
    auto it = v.begin() + itemIndex; 
    std::rotate(it, it + 1, v.end()); 
} 
+1

Questo è ciò che Stepanov (ha progettato la STL) raccomanda: http://www.stepanovpapers.com/notes.pdf, pag. 154. –

+0

Solo una nota: se sto leggendo questo correttamente, questo ha O (n) complessità. La soluzione 'std :: swap' qui sotto ha complessità O (1). – imallett

+1

@imallett Hai ragione. Questa risposta conserva l'ordine degli elementi diversi dall'elemento spostato mentre l'altra risposta non lo fa. La domanda, come affermato, non era chiara se quello fosse un requisito. La conservazione dell'ordine è più costosa. – Blastfurnace

3

È possibile evitare la variabile extra.

v.push_back(v[itemIndex]); 
v.erase(v.begin() + itemIndex); 

Se si elimina spesso a partire dalla metà del vettore e può riscrivere il codice in modo che non richiede l'accesso casuale, si può essere in grado di migliorare l'efficienza utilizzando una lista collegata (std::list), invece.

+0

Ah, naturalmente, pulito! Grazie. A proposito, c'è qualche vantaggio nell'usare 'emplace_back' piuttosto che' push_back' qui? –

+0

@VioletGiraffe, se 'T' è piccolo,' emplace_back' probabilmente non sarà più veloce di 'push_back'. Se 'T' è grande, in realtà non sono sicuro. – Brian

+1

@Brian emplace_back chiama effettivamente il costruttore per la classe, che ridurrebbe al costruttore di copie con questa chiamata ... quindi in questo caso, non penso che sarebbe importante per entrambi. Potresti fare in modo che 'v.push_back (std :: move (v [itemIndex))' si attivi usando il costruttore di mosse in C++ 11. – IdeaHat

5

Forse il modo più veloce, sarebbe quello di scambiarlo con l'ultimo elemento

template <typename T> 
void moveItemToBack(std::vector<T>& v, size_t itemIndex) 
{ 
    std::swap(v[itemIndex], v.back()); // or swap with *(v.end()-1) 
} 

una sola operazione! Ofcourse std::swap deve funzionare con T

+3

È una soluzione ovvia, ma altera l'ordine degli oggetti oltre il semplice spostamento di uno alla fine. –

+0

@VioletGiraffe Mentre ruotare non lo fa? –

+0

@VioletGiraffe OK ora ha quello che vuoi. Va bene ruotare finché l'ordine relativo non cambia.Stavo semplicemente dando una risposta letterale alla domanda "sposta un elemento sul retro" –

Problemi correlati