Dove dovrei usare un Deque nel software di produzione?


21

Conosco abbastanza bene dove usare pile, code e alberi nelle applicazioni software ma non ho mai usato una Deque (coda doppia) prima. Dove li incontro di solito in natura? Sarebbe negli stessi posti di una coda ma con ulteriori problemi?



sembra che ci sia un po 'di confusione in questo thread. Su Internet "deque" è una coda doppia (Wikipedia menziona l'implementazione dell'elenco collegato). Tuttavia in C ++ STL, "std :: deque" è una struttura simile ad array implementata come array di blocchi di dati. Offre un accesso casuale, simile a std :: vector, ma la sua capacità di ridimensionamento è più vicina a quella di std :: list perché quando vengono aggiunti i dati, aggiunge blocchi e non rialloca e sposta i dati esistenti.
DXM,

1
@DXM: una deque STL è comunque una coda a doppia estremità e fornisce operazioni più veloci alle estremità (a seconda dell'implementazione). Il fatto che offra anche l'accesso al centro non rende la sua operazione primaria meno simile alla coda.
gbjbaanb

@gbjbaanb: Tutto quello che sto dicendo è se guardi le interfacce pubbliche di 3 classi: std :: vector, std :: list (o std :: queue) e std :: deque, vedrai che std :: vector e std :: deque hanno identica interfaccia pubblica e identiche capacità (std :: deque è leggermente più flessibile a scapito di un ingombro di memoria maggiore). d'altra parte std :: list e std :: queue si comportano in modo più simile alle loro controparti CS, alle liste collegate e alla coda rispettivamente. CS deque! = Std :: deque
DXM

Trovo questa risposta più pratico da shiv.mymail - stackoverflow.com/questions/3880254/...
roottraveller

Risposte:


21

Un modo in cui viene usato un deque è "invecchiare" gli oggetti. In genere viene utilizzato come funzionalità di annullamento o cronologia. Una nuova azione viene inserita nel deque. Gli oggetti più vecchi sono nella parte anteriore. Un limite alle dimensioni del deque impone la rimozione degli oggetti nella parte anteriore quando vengono inseriti nuovi oggetti (invecchiamento degli oggetti più vecchi). Fornisce quindi un modo rapido per accedere a entrambe le estremità della struttura perché si conoscono all'istante gli elementi più vecchi e più recenti per rimuovere il fronte e commettere l'azione più vecchia in O (1) o annullare in O (1).


Non penso che i deques siano usati / necessari qui. Uno stack semplice (forse di dimensioni limitate) è sufficiente.
Konrad Rudolph,

2
@Konrad Come invecchiate gli oggetti in un semplice stack? (es. come si rimuovono i comandi "troppo vecchi"?)
Andres F.

@AndresF. L'età è indipendente dalle dimensioni dello stack? In tal caso, non ho mai sentito parlare di questa struttura di dati. Altrimenti è semplicemente uno stack di dimensioni fisse che può essere implementato in termini di deque, o semplicemente postulando una struttura dati più semplice chiamata stack di dimensioni fisse.
Konrad Rudolph,

Quindi i deques sono utili dopo tutto;) Mai sentito parlare di una pila di dimensioni fisse (nel senso che intendi, di rimuovere l'oggetto più vecchio). Ha senso, ma non è ciò che di solito si intende per "pila", e se è effettivamente più semplice rimane da vedere :)
Andres F.

Questo è stato pubblicato nei commenti della domanda originale: en.wikipedia.org/wiki/Double-ended_queue È solo una doppia coda. L'ho usato in pratica nel modo che ho descritto sopra (motivo per cui l'ho pubblicato). In un vero stack, le uniche operazioni che dovresti avere sono push, pop, top e peek (potremmo discutere degli altri, ma in genere è così). Non dovresti avere conoscenza di ciò che si trova in fondo alla pila o di come accedere al fondo. In una "pila di dimensioni fisse" genereresti semplicemente un overflow della pila invece di invecchiare vecchi oggetti quando lo riempivi.
jmq

1

Ottima domanda Non ricordo il nostro corso CS 102 che menzionava una singola applicazione per la coda doppia.

Fino ad oggi, l'unica applicazione che conosco è lo scheduler che ruba il lavoro menzionato nell'articolo di Wikipedia .

Funziona essenzialmente come segue:

In un normale modello procedurale a thread singolo, ogni chiamata di funzione invia un record di attivazione su un cosiddetto stack di chiamate . Un record di attivazione contiene le variabili e i parametri locali di quella chiamata. Una volta completata la chiamata al metodo ("restituisce"), l'ultimo record di attivazione viene estratto dallo stack di chiamate.

Ciò è particolarmente importante perché è così che viene implementata la ricorsione: la struttura della ricorsione è rappresentata nello stato corrente dello stack di chiamate.

Quando si parallelizza un algoritmo ricorsivo, possiamo sfruttare questa proprietà sostituendo lo stack di chiamate con una coda di chiamata. Ogni thread nel calcolo ottiene la propria coda di chiamate e invia e apre i record di attivazione come in un'esecuzione sequenziale.

Ma una volta che un thread ha terminato il suo lavoro (= la sua coda di chiamata è vuota), ruba il lavoro da un altro thread rimuovendo un record di attivazione dalla coda di chiamata di quel thread rimuovendo dall'estremità "errata".

Fondamentalmente, la coda di chiamata funge da due stack di chiamate che ora servono due thread.


Hai una fonte per questo? Sembra interessante.
kyjelly90210

1
@ Richard1987 L'articolo di Wikipedia cita l'articolo originale. Esistono diverse implementazioni, ad esempio nell'estensione GNU c ++ stdlib parallela che ruba il lavoro (ma il codice è orribile da leggere) o un'implementazione parallela di divisione e conquista generalizzata da parte tua (ma quest'ultima utilizza uno stile di programmazione molto idiomatico particolare per la biblioteca e quindi difficile da leggere)
Konrad Rudolph
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.