Apuntes DAM
Volver al inicio

Cola (queue)

AlgoritmosEstructuras de datosNivel básicoTambién: queue, FIFO, cola circular

Una colección en la que se entra por el final y se sale por el principio: el primero que llega es el primero que sale (FIFO). Colas de impresión, de mensajes, de procesos o el recorrido en anchura.

Visualízalo paso a paso

Cambia los datos, dale a reproducir y sigue cada paso en el dibujo, en la línea de Java que se ejecuta y en sus variables.

Cola circular

Escribe las operaciones (encolar un nombre o desencolar) y mira cómo frente y fin dan la vuelta al array sin mover a nadie.

encolar Nombre o desencolar, separadas por comas
  • fuera de juego

Paso 1

Un array de 5 casillas. frente señala al primero que saldrá y fin a la casilla donde entrará el siguiente; al pasar de la última, vuelven a la 0 (por eso es circular).

1class ColaCircular {
2    private final String[] a;
3    private int frente = 0, fin = 0, tam = 0;
4
5    ColaCircular(int capacidad) { a = new String[capacidad]; }
6
7    boolean encolar(String x) {
8        if (tam == a.length) return false;
9        a[fin] = x;
10        fin = (fin + 1) % a.length;
11        tam++;
12        return true;
13    }
14
15    String desencolar() {
16        if (tam == 0) return null;
17        String x = a[frente];
18        a[frente] = null;
19        frente = (frente + 1) % a.length;
20        tam--;
21        return x;
22    }
23}

Variables

frente
0
fin
0
tam
0 de 5

Atajos con el foco dentro del visualizador: ← → paso a paso, Espacio reproducir o pausar, Inicio/Fin ir al principio o al final.

La idea

Una cola funciona como la de un supermercado: los elementos entran por el final (encolar) y salen por el principio (desencolar), así que salen en el mismo orden en que llegaron. Es FIFO: first in, first out.

Implementarla sobre un array tiene una trampa. Si al desencolar se mueven todos una posición a la izquierda, cada salida cuesta O(n). Si no se mueven y solo avanza el índice del frente, el array se va quedando vacío por la izquierda mientras se llena por la derecha.

La solución es el array circular: dos índices, frente y final, que avanzan siempre hacia la derecha y, al pasar de la última casilla, vuelven a la 0 con (i + 1) % capacidad. Nadie se mueve nunca de su casilla, así que encolar y desencolar son O(1); el array solo se copia a otro más grande cuando se llena de verdad.

Las colas aparecen siempre que algo se produce más rápido de lo que se consume: documentos que esperan a la impresora, peticiones que esperan a un servidor, procesos que esperan a la CPU, mensajes entre microservicios. También son la pieza del recorrido en anchura (BFS) de árboles y grafos.

Cuándo usarlo

  • Atender peticiones, tareas o mensajes en el orden en que llegan.
  • Comunicar un hilo que produce datos con otro que los consume (BlockingQueue).
  • Recorrer un árbol o un grafo por niveles (BFS) y calcular distancias mínimas sin pesos.
  • Planificar procesos por turnos (Round Robin) o simular colas reales.

Cuándo no

  • Si importa la prioridad y no el orden de llegada: una cola de prioridad (montículo).
  • Si se necesita lo último que entró: una pila.

Paso a paso

  1. Encolar (offer). Se pone el elemento en la posición final, a[(frente + tam) % capacidad], y tam++. Si está llena, se copia a un array mayor.
  2. Desencolar (poll). Se devuelve a[frente], se avanza frente = (frente + 1) % capacidad y tam--. Nadie más se mueve.
  3. Consultar el primero (peek). Se mira a[frente] sin sacarlo.
  4. Crecer. Al copiar a un array mayor se copian en orden, empezando por el frente, para que la parte que había dado la vuelta quede seguida.

El código

Una cola circular genérica

La cola de una impresora: el array empieza con 4 casillas y los índices dan la vuelta; si se llenara, crecería.

Java
1import java.util.NoSuchElementException;
2
3public class Main {
4    /** Una cola sobre un array circular: frente y final dan la vuelta al llegar al final del array. */
5    static class Cola<T> {
6        private Object[] a = new Object[4];
7        private int frente = 0, tam = 0;
8
9        void encolar(T x) {
10            if (tam == a.length) crecer();
11            a[(frente + tam) % a.length] = x;          // el final es frente + tam, dando la vuelta
12            tam++;
13        }
14
15        @SuppressWarnings("unchecked")
16        T desencolar() {
17            if (tam == 0) throw new NoSuchElementException("cola vacía");
18            T x = (T) a[frente];
19            a[frente] = null;
20            frente = (frente + 1) % a.length;
21            tam--;
22            return x;
23        }
24
25        boolean vacia() { return tam == 0; }
26
27        private void crecer() {                       // se copia en orden a un array el doble de grande
28            Object[] b = new Object[2 * a.length];
29            for (int i = 0; i < tam; i++) b[i] = a[(frente + i) % a.length];
30            a = b;
31            frente = 0;
32        }
33    }
34
35    public static void main(String[] args) {
36        Cola<String> impresora = new Cola<>();
37        for (String doc : new String[] {"apuntes.pdf", "foto.png", "examen.docx"}) {
38            impresora.encolar(doc);
39            System.out.println("En cola: " + doc);
40        }
41        System.out.println("Imprimiendo " + impresora.desencolar());
42        impresora.encolar("factura.pdf");
43        System.out.println("En cola: factura.pdf");
44        while (!impresora.vacia()) System.out.println("Imprimiendo " + impresora.desencolar());
45    }
46}

Salida al ejecutarlo (la misma en los 5 lenguajes)

En cola: apuntes.pdf
En cola: foto.png
En cola: examen.docx
Imprimiendo apuntes.pdf
En cola: factura.pdf
Imprimiendo foto.png
Imprimiendo examen.docx
Imprimiendo factura.pdf

Las colas de la biblioteca

Lo habitual es usar la de la biblioteca, que ya es un array circular.

Java
1// En Java, la cola de la biblioteca es ArrayDeque (o LinkedList) a través de la interfaz Queue
2Queue<String> cola = new ArrayDeque<>();
3cola.offer("Ana");                 // encolar por el final
4cola.offer("Luis");
5String primero = cola.peek();      // "Ana", sin sacarlo
6String sale = cola.poll();         // "Ana"; poll devuelve null si está vacía (remove lanzaría excepción)

Traza: cola circular de capacidad 4

Operacióna[0..3]frentefinaltam
encolar AA · · ·011
encolar BA B · ·022
encolar CA B C ·033
desencolar → A· B C ·132
desencolar → B· · C ·231
encolar D· · C D202
encolar EE · C D213
encolar FE F C D224

Al encolar E y F, el final da la vuelta a las casillas 0 y 1, que se habían quedado libres: el array se reutiliza sin mover a nadie.

Complejidad

OperaciónArray circularArray desplazandoLista enlazada
EncolarO(1) amortizadoO(1)O(1) con referencia al último
DesencolarO(1)O(n)O(1)
Consultar el primeroO(1)O(1)O(1)

Memoria: O(n). ArrayDeque es un array circular; LinkedList también sirve como cola, pero cada nodo ocupa bastante más memoria.

0102030405015101520tamaño de la entrada (n)operacionesO(n!)O(2ⁿ)O(n²)O(n log n)O(1)O(log n)O(n)
  • Mejor caso: O(1)
  • Caso medio: O(1)
  • Peor caso: O(n)

Encolar y desencolar son O(1) con un array circular; solo crecer (copiar a un array mayor) cuesta n. Las curvas grises son las demás clases, para comparar.

En la práctica

  • La cola de impresión, la de envío de correos o la de tareas en segundo plano de cualquier aplicación web.
  • El planificador de un sistema operativo mantiene una cola de procesos listos; Round Robin es literalmente una cola con turnos de tiempo fijo.
  • Los sistemas de mensajería (RabbitMQ, Kafka, SQS) son colas entre programas que no tienen por qué estar funcionando a la vez.
  • BlockingQueue comunica hilos productores y consumidores en Java sin carreras.

Errores típicos

  • Desencolar moviendo todos los elementos a la izquierda (for que desplaza): convierte cada salida en O(n).
  • Olvidar el % capacidad al avanzar un índice: se sale del array.
  • Distinguir mal «llena» de «vacía» cuando frente == final: por eso se guarda también tam.
  • En JavaScript, usar array.shift() como desencolar en colas grandes: desplaza todo el array cada vez.
  • Con Queue, usar remove()/element() en vez de poll()/peek() sin comprobar si está vacía: lanzan excepción.

Ejercicios

Cada ejercicio se corrige solo con sus pruebas (algunas ocultas). Escribe tu solución en el editor y pulsa Ejecutar o Comprobar; la solución explicada está debajo, por si te atascas.

1. Una ventanilla y sus tiempos de espera

Hay una sola ventanilla y los clientes se atienden por orden de llegada. Cada línea es un cliente: nombre, minuto de llegada y minutos que tarda su gestión (las llegadas no bajan nunca). Calcula cuánto espera cada uno, cuándo sale, la espera total y quién esperó más. El main ya lee y valida y mete los clientes en una cola: completa atender.

  • Entrada: líneas como Ana 0 5 (llega en el minuto 0 y tarda 5).
  • Salida: Ana: espera 0 min y sale en el minuto 5 por cliente y, al final, Espera total: T min · la más larga: M min (Nombre) (si empatan, el primero).
  • Errores: Línea no válida: «…» (también si llega antes que el anterior) y No hay clientes.
☕JavaUna ventanilla y sus tiempos de esperaMedio

Ejemplo

Entrada (lo que se escribe por teclado)
Ana 0 5
Luis 1 3
Eva 2 4
Pablo 20 2
Salida esperada
Ana: espera 0 min y sale en el minuto 5
Luis: espera 4 min y sale en el minuto 8
Eva: espera 6 min y sale en el minuto 12
Pablo: espera 0 min y sale en el minuto 22
Espera total: 10 min · la más larga: 6 min (Eva)
⏳
Test oculto #3
⏳
Test oculto #4
⏳
Test oculto #5
0/5 tests pasados · pulsa un test para ver su entrada y su salida esperada
Ver la solución explicada
java
1import java.util.*;
2
3public class Main {
4    record Cliente(String nombre, int llegada, int duracion) { }
5
6    /** Atiende a todos por orden de llegada con una sola ventanilla y escribe la espera de cada uno.
7        Devuelve {espera total, espera máxima, posición en la cola del que más esperó}. */
8    static int[] atender(Queue<Cliente> cola) {
9        int reloj = 0, total = 0, maxima = -1, quien = -1, k = 0;
10        while (!cola.isEmpty()) {
11            Cliente c = cola.poll();
12            int empieza = Math.max(reloj, c.llegada());   // si la ventanilla está libre, empieza al llegar
13            int espera = empieza - c.llegada();
14            reloj = empieza + c.duracion();
15            System.out.println(c.nombre() + ": espera " + espera + " min y sale en el minuto " + reloj);
16            total += espera;
17            if (espera > maxima) {
18                maxima = espera;
19                quien = k;
20            }
21            k++;
22        }
23        return new int[] {total, maxima, quien};
24    }
25
26    public static void main(String[] args) {
27        Scanner sc = new Scanner(System.in);
28        Queue<Cliente> cola = new ArrayDeque<>();
29        List<String> nombres = new ArrayList<>();
30        int ultima = 0;
31        while (sc.hasNextLine()) {
32            String linea = sc.nextLine().trim();
33            if (linea.isEmpty()) continue;
34            String[] p = linea.split("\\s+");
35            if (p.length != 3 || !p[1].matches("\\d{1,4}") || !p[2].matches("[1-9]\\d{0,2}") || Integer.parseInt(p[1]) < ultima) {
36                System.out.println("Línea no válida: «" + linea + "»");
37                continue;
38            }
39            ultima = Integer.parseInt(p[1]);
40            cola.offer(new Cliente(p[0], ultima, Integer.parseInt(p[2])));
41            nombres.add(p[0]);
42        }
43        if (cola.isEmpty()) {
44            System.out.println("No hay clientes");
45            return;
46        }
47        int[] r = atender(cola);
48        System.out.println("Espera total: " + r[0] + " min · la más larga: " + r[1] + " min (" + nombres.get(r[2]) + ")");
49    }
50}

La cola garantiza el orden de llegada y el reloj resume todo lo demás: es una simulación de eventos discretos, la técnica con la que se dimensionan cajas, servidores o centralitas.

Si la ventanilla queda libre antes de que llegue el siguiente, el reloj salta a su llegada y su espera es 0.

2. Planificación Round Robin

El planificador Round Robin reparte la CPU por turnos: el primer proceso de la cola la usa como mucho q unidades de tiempo (el quantum); si no ha terminado, vuelve al final de la cola. Todos los procesos llegan en el instante 0, en el orden de la entrada. Escribe el diagrama de Gantt, cuándo termina cada proceso y la suma de esos tiempos. Completa planificar.

  • Entrada: línea 1 el quantum (1 a 99); después, una línea por proceso con su nombre y su duración (1 a 99).
  • Salida: el diagrama en una línea, como P1(0-2) P2(2-4) P1(4-5); luego P1 termina en 5 por proceso (en el orden de la entrada) y Suma de los tiempos de finalización: S.
  • Errores: Quantum no válido: «…», Proceso no válido: «…» (también un nombre repetido) y No hay procesos.
☕JavaPlanificación Round RobinDifícil

Ejemplo

Entrada (lo que se escribe por teclado)
2
P1 5
P2 3
P3 1
Salida esperada
P1(0-2) P2(2-4) P3(4-5) P1(5-7) P2(7-8) P1(8-9)
P1 termina en 9
P2 termina en 8
P3 termina en 5
Suma de los tiempos de finalización: 22
⏳
Test oculto #3
⏳
Test oculto #4
⏳
Test oculto #5
0/5 tests pasados · pulsa un test para ver su entrada y su salida esperada
Ver la solución explicada
java
1import java.util.*;
2
3public class Main {
4    static final class Proceso {
5        final String nombre;
6        int resta;
7        Proceso(String nombre, int duracion) { this.nombre = nombre; this.resta = duracion; }
8    }
9
10    /** Round Robin: cada proceso usa la CPU como mucho q unidades y, si no ha terminado, vuelve al
11        final de la cola. Devuelve el diagrama (P1(0-2) P2(2-4) …) y apunta en fin cuándo acaba cada uno. */
12    static String planificar(Queue<Proceso> cola, int q, Map<String, Integer> fin) {
13        StringJoiner gantt = new StringJoiner(" ");
14        int t = 0;
15        while (!cola.isEmpty()) {
16            Proceso p = cola.poll();
17            int usa = Math.min(q, p.resta);
18            gantt.add(p.nombre + "(" + t + "-" + (t + usa) + ")");
19            t += usa;
20            p.resta -= usa;
21            if (p.resta > 0) cola.offer(p);
22            else fin.put(p.nombre, t);
23        }
24        return gantt.toString();
25    }
26
27    public static void main(String[] args) {
28        Scanner sc = new Scanner(System.in);
29        String primera = sc.hasNextLine() ? sc.nextLine().trim() : "";
30        if (!primera.matches("[1-9]\\d?")) {
31            System.out.println("Quantum no válido: «" + primera + "»");
32            return;
33        }
34        int q = Integer.parseInt(primera);
35        Queue<Proceso> cola = new ArrayDeque<>();
36        List<String> orden = new ArrayList<>();
37        while (sc.hasNextLine()) {
38            String linea = sc.nextLine().trim();
39            if (linea.isEmpty()) continue;
40            String[] p = linea.split("\\s+");
41            if (p.length != 2 || !p[1].matches("[1-9]\\d?") || orden.contains(p[0])) {
42                System.out.println("Proceso no válido: «" + linea + "»");
43                continue;
44            }
45            cola.offer(new Proceso(p[0], Integer.parseInt(p[1])));
46            orden.add(p[0]);
47        }
48        if (cola.isEmpty()) {
49            System.out.println("No hay procesos");
50            return;
51        }
52        Map<String, Integer> fin = new HashMap<>();
53        System.out.println(planificar(cola, q, fin));
54        int suma = 0;
55        for (String n : orden) {
56            System.out.println(n + " termina en " + fin.getOrDefault(n, 0));
57            suma += fin.getOrDefault(n, 0);
58        }
59        System.out.println("Suma de los tiempos de finalización: " + suma);
60    }
61}

Round Robin es una cola en la que los elementos pueden volver a entrar: nadie espera más de (n − 1)·q unidades por turno, por eso da buen tiempo de respuesta a los procesos interactivos.

Con un quantum muy grande se comporta como FCFS (el primero que llega, hasta el final); con uno muy pequeño, los cambios de contexto se comen la CPU.

Test

Test: Cola (queue)

0/5 respondidas · 0 aciertos

Elige una respuesta en cada pregunta: verás al momento si es correcta y por qué. Con un 80 % de aciertos se da por superada.

  1. 1.Se encolan A, B y C y se desencola una vez. ¿Quién sale?

  2. 2.En una cola circular de capacidad 5, el final está en la posición 4. ¿Dónde se pondrá el siguiente?

  3. 3.¿Por qué no conviene desencolar desplazando todos los elementos?

  4. 4.¿Qué método de Queue devuelve null si la cola está vacía en lugar de lanzar una excepción?

  5. 5.¿Qué recorrido de un grafo usa una cola?

Relacionado