Qual è la differenza tra i contenitori deque e list STL?


93

Qual è la differenza tra i due? Voglio dire, i metodi sono tutti uguali. Quindi, per un utente, funzionano in modo identico.

È corretto??


1
Sono interessato alle prestazioni di iterazione .. cosa è più veloce da ierare dall'inizio alla fine?
nkint

Risposte:


60

Dal sommario SGI STL (datato ma ancora molto utile) di deque:

Un deque è molto simile a un vettore: come il vettore, è una sequenza che supporta l'accesso casuale agli elementi, l'inserimento e la rimozione del tempo costante di elementi alla fine della sequenza e l'inserimento e la rimozione del tempo lineare di elementi nel mezzo.

Il modo principale in cui deque differisce dal vettore è che deque supporta anche l'inserimento e la rimozione a tempo costante di elementi all'inizio della sequenza. Inoltre, deque non ha funzioni membro analoghe a capacity () e reserve () di vector e non fornisce nessuna delle garanzie sulla validità dell'iteratore associate a tali funzioni membro.

Ecco il riepilogo listdallo stesso sito:

Una lista è una lista doppiamente collegata. Cioè, è una sequenza che supporta sia l'attraversamento in avanti che all'indietro e l'inserimento e la rimozione di tempo costante (ammortizzato) di elementi all'inizio o alla fine o nel mezzo. Gli elenchi hanno l'importante proprietà che l'inserimento e lo splicing non invalidano gli iteratori degli elementi dell'elenco e che anche la rimozione invalida solo gli iteratori che puntano agli elementi rimossi. L'ordine degli iteratori può essere modificato (ovvero, list :: iterator potrebbe avere un predecessore o successore diverso dopo un'operazione di elenco rispetto a prima), ma gli iteratori stessi non verranno invalidati o puntati a elementi diversi a meno che tale invalidazione o la mutazione è esplicita.

In sintesi, i contenitori possono avere routine condivise ma le garanzie di tempo per tali routine differiscono da contenitore a contenitore . Questo è molto importante quando si considera quale di questi contenitori utilizzare per un'attività: tenere conto di come il contenitore verrà utilizzato più frequentemente (ad esempio, più per la ricerca che per l'inserimento / cancellazione) fa molto per indirizzarti al contenitore giusto .


2
std :: list ha anche il metodo 'splice' che ti consente di unire due elenchi insieme
Rick

23
In realtà, le garanzie di tempo sono la seconda caratteristica più importante della lista. La caratteristica più importante di list è che puoi aggiungere e rimuovere elementi e non invalidare i tuoi iteratori! In (quasi?) Ogni altro contenitore STL, ogni operazione di modifica invalida tutti i tuoi iteratori, quindi per "eliminare gli elementi corrispondenti" devi accumulare gli elementi corrispondenti in un'operazione e quindi eliminarli in un'altra. In un elenco, puoi esaminarlo, rimuovere e aggiungere come desideri e non dover mai ricalcolare un iteratore.
Tom Swirly

1
Queste sono anche le differenze astratte, quindi misura la realtà per il tuo caso! Sia list che deque hanno inserimento / cancellazione O (1), ma non dimenticare che significa k * O (1) e k ha valori diversi per list e deque. Nel mio caso, ci è voluto dieci volte più tempo per aggiungere un oggetto a un elenco rispetto a un deque perché l'elenco necessitava di più chiamate per nuovo / eliminare. Ciò ovviamente varierà in base all'implementazione STL che hai.
Andy Krouwel

125

Lasciami elencare le differenze:

  • Deque gestisce i suoi elementi con un array dinamico , fornisce l'accesso casuale e ha quasi la stessa interfaccia di un vettore.
  • L'elenco gestisce i suoi elementi come un elenco a doppio collegamento e non fornisce l'accesso casuale .

  • Deque fornisce inserimenti ed eliminazioni veloci sia alla fine che all'inizio. L'inserimento e l'eliminazione di elementi nel mezzo è relativamente lento perché tutti gli elementi fino a una delle due estremità possono essere spostati per fare spazio o per riempire uno spazio vuoto.
  • In List , l'inserimento e la rimozione di elementi è veloce in ogni posizione, incluse entrambe le estremità.

  • Deque : qualsiasi inserimento o cancellazione di elementi diversi da quelli all'inizio o alla fine invalida tutti i puntatori, i riferimenti e gli iteratori che si riferiscono agli elementi del deque.
  • Elenco : l'inserimento e l'eliminazione di elementi non invalida i puntatori, i riferimenti e gli iteratori ad altri elementi.

Complessità

             Insert/erase at the beginning       in middle        at the end

Deque:       Amortized constant                  Linear           Amortized constant
List:        Constant                            Constant         Constant

5
@aJ: Qual è la differenza tra constante amortized constant?
Lazer

16
Le operazioni a lungo termine si comportano come descritto. Tuttavia, una singola operazione potrebbe richiedere più tempo di quanto specificato. es: inserire un elemento in un vettore la cui capacità attuale è 10 e la dimensione già 9 è costante, dove il tempo è lineare se la capacità è 10 e anche la dimensione è 10. È perché deve allocare e copiare tutti gli elementi nella nuova memoria .
aJ.

5
@aJ: In che modo deque fornisce l'accesso casuale? Inoltre come viene implementata questa struttura?

9

std::list è fondamentalmente una lista doppiamente collegata.

std::deque, d'altra parte, è implementato più simile std::vector. Ha un tempo di accesso costante per indice, così come l'inserimento e la rimozione all'inizio e alla fine, che fornisce caratteristiche di prestazioni notevolmente diverse rispetto a un elenco.


5

Un'altra importante garanzia è il modo in cui ogni diverso contenitore archivia i propri dati in memoria:

  • Un vettore è un singolo blocco di memoria contiguo.
  • Un deque è un insieme di blocchi di memoria collegati, in cui più di un elemento è memorizzato in ogni blocco di memoria.
  • Una lista è un insieme di elementi dispersi in memoria, cioè: solo un elemento viene memorizzato per "blocco" di memoria.

Nota che il deque è stato progettato per provare a bilanciare i vantaggi di vector e list senza i rispettivi inconvenienti. È un contenitore particolarmente interessante nelle piattaforme con memoria limitata, ad esempio i microcontrollori.

La strategia di archiviazione della memoria viene spesso trascurata, tuttavia, è spesso uno dei motivi più importanti per selezionare il contenitore più adatto per una determinata applicazione.


4

No. Un deque supporta solo l'inserimento e l'eliminazione di O (1) nella parte anteriore e posteriore. Può, ad esempio, essere implementato in un vettore con avvolgimento. Dal momento che garantisce anche l'accesso casuale O (1), puoi essere certo che non sta usando (solo) una lista doppiamente collegata.


2

Le differenze di prestazioni sono state spiegate bene da altri. Volevo solo aggiungere che interfacce simili o addirittura identiche sono comuni nella programmazione orientata agli oggetti, parte della metodologia generale di scrittura di software orientato agli oggetti. Non dovresti IN NESSUN MODO presumere che due classi funzionino allo stesso modo semplicemente perché implementano la stessa interfaccia, non più di quanto dovresti presumere che un cavallo funzioni come un cane perché entrambe implementano attack () e make_noise ().


1

Ecco un codice proof-of-concept dell'uso di lista, mappa non ordinata che fornisce la ricerca O (1) e la manutenzione esatta di O (1) LRU. Ha bisogno degli iteratori (non cancellati) per sopravvivere alle operazioni di cancellazione. Pianificare l'utilizzo in una cache gestita da software arbitrariamente grande O (1) per i puntatori della CPU sulla memoria della GPU. Annuisce allo scheduler Linux O (1) (coda di esecuzione LRU <-> per processore). Unordered_map ha un accesso temporale costante tramite una tabella hash.

#include <iostream> 
#include <list> 
#include <unordered_map>  
using namespace std; 

struct MapEntry {
  list<uint64_t>::iterator LRU_entry;
  uint64_t CpuPtr;
};
typedef unordered_map<uint64_t,MapEntry> Table;
typedef list<uint64_t> FIFO;
FIFO  LRU;        // LRU list at a given priority 
Table DeviceBuffer; // Table of device buffers

void Print(void){
  for (FIFO::iterator l = LRU.begin(); l != LRU.end(); l++) {
    std::cout<< "LRU    entry "<< *l << "   :    " ;
    std::cout<< "Buffer entry "<< DeviceBuffer[*l].CpuPtr <<endl;
  }  
}
int main() 
{ 

  LRU.push_back(0);
  LRU.push_back(1);
  LRU.push_back(2);
  LRU.push_back(3);
  LRU.push_back(4);

  for (FIFO::iterator i = LRU.begin(); i != LRU.end(); i++) {
    MapEntry ME = { i, *i}; 
    DeviceBuffer[*i] = ME;
  }

  std::cout<< "************ Initial set of CpuPtrs" <<endl;
  Print();

  {
    // Suppose evict an entry - find it via "key - memory address uin64_t" and remove from 
    // cache "tag" table AND LRU list with O(1) operations
    uint64_t key=2;
    LRU.erase(DeviceBuffer[2].LRU_entry);
    DeviceBuffer.erase(2);
  }

  std::cout<< "************ Remove item 2 " <<endl;
  Print();

  { 
    // Insert a new allocation in both tag table, and LRU ordering wiith O(1) operations
    uint64_t key=9;
    LRU.push_front(key); 
    MapEntry ME = { LRU.begin(), key };
    DeviceBuffer[key]=ME;
  }

  std::cout<< "************ Add item 9  " <<endl;
  Print();

  std::cout << "Victim "<<LRU.back()<<endl;
} 

L'hai pubblicato nel posto giusto? Questo non risponde alla domanda.
Blastfurnace

1

Tra eminenti differenze tra dequeelist

  • Per deque :

    Articoli conservati fianco a fianco;

    Ottimizzato per l'aggiunta di dati da due lati (fronte, retro);

    Elementi indicizzati da numeri (numeri interi).

    Può essere esplorato dagli iteratori e anche dall'indice dell'elemento.

    L'accesso ai dati è più veloce.

  • Per list

    Elementi archiviati "casualmente" nella memoria;

    Può essere esplorato solo dagli iteratori;

    Ottimizzato per l'inserimento e la rimozione nel mezzo.

    L'accesso temporale ai dati è più lento, lento nell'iterazione, a causa della sua posizione spaziale molto scarsa.

    Gestisce molto bene elementi di grandi dimensioni

Puoi controllare anche il seguente link , che confronta le prestazioni tra i due contenitori STL (con std :: vector)

Spero di aver condiviso alcune informazioni utili.

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.