Coda (queue)
Ultimo aggiornamento
Una coda ha due estremità attive. I nuovi valori entrano dal fondo ed escono dalla testa, quindi chi ha aspettato più a lungo viene servito per primo. Questo è il FIFO, ed è esattamente come funziona una fila allo sportello: mettersi in fondo ed essere serviti dalla testa è ciò che rende l'attesa equa. Premi Play qui sopra e guarda i valori entrare da un lato e uscire dall'altro.
Dato che ogni estremità è tracciata da un suo indice o puntatore, entrambe le operazioni sono O(1) e nessuna delle due sposta il resto dei dati. Per questo le code stanno sotto tutto ciò che elabora il lavoro in ordine di arrivo: lavori di stampa, code di task e di messaggi, buffer delle richieste e la visita in ampiezza, che visita un grafo livello per livello proprio perché tiene la sua frontiera in una coda. Sposta l'estremità di rimozione in fondo e ottieni invece una pila.
Complessità temporale e spaziale
Per una coda basata su un buffer circolare o su una lista concatenata, le due implementazioni standard:
| Operazione | Complessità | Note |
|---|---|---|
| Enqueue | O(1) | Scrivi in fondo e fai avanzare l'indice di fondo. |
| Dequeue | O(1) | Leggi in testa e fai avanzare l'indice di testa, senza spostamenti. |
| Peek (testa) | O(1) | Leggi il valore in testa senza rimuoverlo. |
| Ricerca | O(n) | Non è lo scopo di una coda: per guardarci dentro devi svuotarla. |
| Spazio | O(n) | Una posizione per ogni valore in attesa. |
Passo dopo passo
| Passo | Cosa succede |
|---|---|
| 1 | La coda parte vuota, con testa e fondo che puntano alla stessa posizione. |
| 2 | Enqueue scrive il valore in fondo, poi fa avanzare il fondo di uno. |
| 3 | Ogni enqueue successivo finisce dietro i valori già in attesa. |
| 4 | Dequeue legge il valore in testa, poi fa avanzare la testa di uno. |
| 5 | Il valore restituito è sempre quello che ha aspettato più a lungo. |
| 6 | Quando la testa raggiunge il fondo la coda è di nuovo vuota, e un ulteriore dequeue è un errore. |
Esempio svolto
Enqueue di 3, 7, 5 e poi svuotamento della coda:
| Operazione | Coda (dalla testa al fondo) | Restituisce |
|---|---|---|
enqueue(3) | [3] | niente |
enqueue(7) | [3, 7] | niente |
enqueue(5) | [3, 7, 5] | niente |
dequeue() | [7, 5] | 3, il valore più vecchio |
dequeue() | [5] | 7 |
dequeue() | [] | 5, il valore più recente, per ultimo |
Quando usare una coda
| Usala quando | Evitala quando |
|---|---|
| Il lavoro va gestito in ordine di arrivo: code di lavori, buffer delle richieste, spooler di stampa | Ti serve prima l'elemento più recente, cioè una pila |
| Stai esplorando livello per livello, come fa la visita in ampiezza | Gli elementi vanno serviti per priorità e non per arrivo, e lì è adatto un heap |
| Un produttore e un consumatore vanno a velocità diverse e serve un buffer tra loro | Ti serve cercare o accedere per indice al centro dei dati |
Vuoi inserimento e rimozione O(1) senza spostare elementi | La implementeresti spostando un array a ogni dequeue, cosa che la rende O(n) |
Codice Queue
Un'implementazione di Queue pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.
Codice Queue in Python
1from collections import deque2
3queue = deque()4
5# Enqueue three values at the rear6for value in [3, 7, 5]:7 queue.append(value)8 print(f"enqueue {value} -> {list(queue)}")9
10# Dequeue them from the front: first in, first out11while queue:12 value = queue.popleft()13 print(f"dequeue {value} -> {list(queue)}")14
15print("empty:", len(queue) == 0)Codice Queue in JavaScript
1// A plain array makes dequeue O(n): shift() moves every element left.2// Track a head index instead, the fix the queue article describes.3const queue = { items: [], head: 0 };4
5function enqueue(value) {6 queue.items.push(value);7}8
9function dequeue() {10 const value = queue.items[queue.head];11 queue.items[queue.head] = undefined; // free the slot12 queue.head += 1;13 // Reclaim space once the consumed prefix dominates.14 if (queue.head * 2 >= queue.items.length) {15 queue.items = queue.items.slice(queue.head);16 queue.head = 0;17 }18 return value;19}20
21const size = () => queue.items.length - queue.head;22
23for (const value of [3, 7, 5]) {24 enqueue(value);25 console.log(`enqueue ${value} -> size ${size()}`);26}27
28// Dequeue from the front: first in, first out, amortized O(1)29while (size() > 0) {30 console.log(`dequeue ${dequeue()} -> size ${size()}`);31}32
33console.log('empty:', size() === 0);Codice Queue in Java
1import java.util.ArrayDeque;2import java.util.Queue;3
4public class Main {5 public static void main(String[] args) {6 Queue<Integer> queue = new ArrayDeque<>();7
8 // Enqueue three values at the rear9 for (int value : new int[] {3, 7, 5}) {10 queue.add(value);11 System.out.println("enqueue " + value + " -> " + queue);12 }13
14 // Dequeue them from the front: first in, first out15 while (!queue.isEmpty()) {16 int value = queue.remove();17 System.out.println("dequeue " + value + " -> " + queue);18 }19
20 System.out.println("empty: " + queue.isEmpty());21 }22}Codice Queue in C++
1#include <iostream>2#include <queue>3
4int main() {5 std::queue<int> queue;6
7 // Enqueue three values at the rear8 for (int value : {3, 7, 5}) {9 queue.push(value);10 std::cout << "enqueue " << value << " -> size " << queue.size() << "\n";11 }12
13 // Dequeue them from the front: first in, first out14 while (!queue.empty()) {15 int value = queue.front();16 queue.pop();17 std::cout << "dequeue " << value << " -> size " << queue.size() << "\n";18 }19
20 std::cout << "empty: " << std::boolalpha << queue.empty() << "\n";21 return 0;22}Codice Queue in C
1#include <stdio.h>2
3#define CAP 164
5int queue[CAP];6int front = 0;7int rear = 0; /* index of the next free slot */8
9int main(void) {10 int values[3] = {3, 7, 5};11
12 /* Enqueue three values at the rear */13 for (int i = 0; i < 3; i++) {14 queue[rear++] = values[i];15 printf("enqueue %d -> size %d\n", values[i], rear - front);16 }17
18 /* Dequeue them from the front: first in, first out */19 while (front < rear) {20 int value = queue[front++];21 printf("dequeue %d -> size %d\n", value, rear - front);22 }23
24 printf("empty: %d\n", front == rear);25 return 0;26}Domande frequenti sulla coda
Cosa significa FIFO?
Qual è la differenza tra una coda e una pila?
O(1); una coda rimuove dalla testa (FIFO), una pila rimuove dalla stessa estremità in cui ha aggiunto (LIFO). Per il resto le loro tabelle di complessità sono identiche.Quali sono le operazioni principali di una coda?
enqueue aggiunge un valore in fondo, dequeue rimuove e restituisce il valore in testa, peek (o front) legge la testa senza rimuoverla e is_empty indica se c'è qualcosa in attesa. Tutte e quattro sono O(1).Perché il dequeue è lento se uso un semplice array?
O(n). Le implementazioni reali lo evitano con un buffer circolare che fa avanzare un indice di testa, o con una lista concatenata con un puntatore alla testa. collections.deque di Python e ArrayDeque di Java lo fanno per te, mentre list.pop(0) no.Cos'è una coda circolare?
n continua a funzionare all'infinito invece di uscire dalla fine dell'array.