TechLead
Principiante
18 min
Guía completa

Colas

Estructura de datos cola, principio FIFO e implementación

¿Qué es una cola?

Una cola es una estructura de datos lineal que sigue el principio First In, First Out (FIFO). Piensa en una fila de personas: la primera en llegar es la primera en ser atendida. Los elementos se agregan al final (enqueue) y se eliminan del frente (dequeue).

Las colas son esenciales para gestionar tareas en orden, manejar solicitudes e implementar BFS.

Principio FIFO

← Dequeue (Frente)
[1]
[2]
[3]
Enqueue (Final) →

Primero en entrar, primero en salir — ¡como una fila!

Operaciones de la cola

enqueue(element) - O(1)

Agregar un elemento al final de la cola.

dequeue() - O(1)

Eliminar y devolver el elemento del frente.

front() / peek() - O(1)

Devolver el frente sin eliminarlo.

isEmpty() - O(1)

Comprobar si la cola está vacía.

size() - O(1)

Devolver el número de elementos.

Implementación de cola usando array

class Queue {
  constructor() {
    this.items = [];
  }

  // Add element to rear - O(1)
  enqueue(element) {
    this.items.push(element);
  }

  // Remove element from front - O(n) due to array shift
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items.shift();  // O(n) operation!
  }

  // Get front element - O(1)
  front() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items[0];
  }

  // Check if empty - O(1)
  isEmpty() {
    return this.items.length === 0;
  }

  // Get size - O(1)
  size() {
    return this.items.length;
  }

  // Print queue
  print() {
    console.log(this.items.join(' ← '));
  }
}

// Usage
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.print();  // 1 ← 2 ← 3

console.log(queue.dequeue());  // 1
console.log(queue.front());    // 2
queue.print();  // 2 ← 3

Nota: Array.shift() es O(n) porque debe desplazar todos los elementos. Para mejor rendimiento, usa una cola circular o lista enlazada.

Cola optimizada con objeto (dequeue O(1))

class OptimizedQueue {
  constructor() {
    this.items = {};
    this.frontIndex = 0;
    this.backIndex = 0;
  }

  // Enqueue - O(1)
  enqueue(element) {
    this.items[this.backIndex] = element;
    this.backIndex++;
  }

  // Dequeue - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    
    const item = this.items[this.frontIndex];
    delete this.items[this.frontIndex];
    this.frontIndex++;
    
    return item;
  }

  // Front - O(1)
  front() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items[this.frontIndex];
  }

  isEmpty() {
    return this.backIndex === this.frontIndex;
  }

  size() {
    return this.backIndex - this.frontIndex;
  }

  print() {
    const values = [];
    for (let i = this.frontIndex; i < this.backIndex; i++) {
      values.push(this.items[i]);
    }
    console.log(values.join(' ← '));
  }
}

// This is much more efficient for large queues!

Implementación de cola usando lista enlazada

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkedQueue {
  constructor() {
    this.front = null;
    this.rear = null;
    this.length = 0;
  }

  // Enqueue - O(1)
  enqueue(data) {
    const newNode = new Node(data);
    
    if (this.isEmpty()) {
      this.front = this.rear = newNode;
    } else {
      this.rear.next = newNode;
      this.rear = newNode;
    }
    
    this.length++;
  }

  // Dequeue - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    
    const dequeuedData = this.front.data;
    this.front = this.front.next;
    
    if (this.front === null) {
      this.rear = null;
    }
    
    this.length--;
    return dequeuedData;
  }

  peek() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.front.data;
  }

  isEmpty() {
    return this.length === 0;
  }

  size() {
    return this.length;
  }
}

Cola circular

Una cola circular conecta el final con el inicio, formando un círculo. Esto aprovecha el espacio al implementar una cola con un array de tamaño fijo.

class CircularQueue {
  constructor(capacity) {
    this.items = new Array(capacity);
    this.capacity = capacity;
    this.front = -1;
    this.rear = -1;
    this.currentSize = 0;
  }

  // Check if full - O(1)
  isFull() {
    return this.currentSize === this.capacity;
  }

  // Check if empty - O(1)
  isEmpty() {
    return this.currentSize === 0;
  }

  // Enqueue - O(1)
  enqueue(element) {
    if (this.isFull()) {
      throw new Error('Queue is full');
    }
    
    // First element
    if (this.front === -1) {
      this.front = 0;
    }
    
    // Circular increment
    this.rear = (this.rear + 1) % this.capacity;
    this.items[this.rear] = element;
    this.currentSize++;
  }

  // Dequeue - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    
    const item = this.items[this.front];
    
    // Reset if last element
    if (this.front === this.rear) {
      this.front = this.rear = -1;
    } else {
      // Circular increment
      this.front = (this.front + 1) % this.capacity;
    }
    
    this.currentSize--;
    return item;
  }

  peek() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items[this.front];
  }

  size() {
    return this.currentSize;
  }
}

// Usage
const cq = new CircularQueue(5);
cq.enqueue(1);
cq.enqueue(2);
cq.enqueue(3);
console.log(cq.dequeue());  // 1
cq.enqueue(4);
cq.enqueue(5);
cq.enqueue(6);  // Now using the circular space!

Cola de prioridad

Una cola de prioridad es un tipo especial de cola donde cada elemento tiene una prioridad. Los elementos con mayor prioridad salen antes que los de menor prioridad.

class PriorityQueue {
  constructor() {
    this.items = [];
  }

  // Enqueue with priority - O(n) for simple implementation
  enqueue(element, priority) {
    const queueElement = { element, priority };
    
    // Find correct position based on priority
    let added = false;
    for (let i = 0; i < this.items.length; i++) {
      if (queueElement.priority < this.items[i].priority) {
        this.items.splice(i, 0, queueElement);
        added = true;
        break;
      }
    }
    
    if (!added) {
      this.items.push(queueElement);
    }
  }

  // Dequeue highest priority - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items.shift().element;
  }

  front() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items[0].element;
  }

  isEmpty() {
    return this.items.length === 0;
  }

  size() {
    return this.items.length;
  }

  print() {
    console.log(this.items.map(item => 
      `${item.element}(p:${item.priority})`
    ).join(' ← '));
  }
}

// Usage
const pq = new PriorityQueue();
pq.enqueue('Low priority task', 3);
pq.enqueue('High priority task', 1);
pq.enqueue('Medium priority task', 2);
pq.print();  // High priority task(p:1) ← Medium priority task(p:2) ← Low priority task(p:3)
console.log(pq.dequeue());  // 'High priority task'

Nota: Para mejor rendimiento, las colas de prioridad se implementan normalmente con heaps, que permiten operaciones O(log n).

Problemas comunes de colas

1. Implementar una pila usando colas

class StackUsingQueues {
  constructor() {
    this.q1 = [];
    this.q2 = [];
  }

  push(x) {
    // Add to q2
    this.q2.push(x);
    
    // Move all from q1 to q2
    while (this.q1.length > 0) {
      this.q2.push(this.q1.shift());
    }
    
    // Swap q1 and q2
    [this.q1, this.q2] = [this.q2, this.q1];
  }

  pop() {
    if (this.empty()) {
      throw new Error('Stack is empty');
    }
    return this.q1.shift();
  }

  top() {
    if (this.empty()) {
      throw new Error('Stack is empty');
    }
    return this.q1[0];
  }

  empty() {
    return this.q1.length === 0;
  }
}

2. Generar números binarios de 1 a N

function generateBinaryNumbers(n) {
  const result = [];
  const queue = [];
  
  queue.push('1');
  
  for (let i = 0; i < n; i++) {
    const front = queue.shift();
    result.push(front);
    
    // Generate next binary numbers
    queue.push(front + '0');
    queue.push(front + '1');
  }
  
  return result;
}

console.log(generateBinaryNumbers(5));
// ['1', '10', '11', '100', '101']

3. Primer carácter no repetido en un flujo

function firstNonRepeating(stream) {
  const queue = [];
  const charCount = {};
  const result = [];
  
  for (let char of stream) {
    // Update count
    charCount[char] = (charCount[char] || 0) + 1;
    queue.push(char);
    
    // Remove repeating characters from front
    while (queue.length > 0 && charCount[queue[0]] > 1) {
      queue.shift();
    }
    
    // First non-repeating or -1
    result.push(queue.length > 0 ? queue[0] : '-1');
  }
  
  return result;
}

console.log(firstNonRepeating('aabcbc'));
// ['a', '-1', 'b', 'b', 'b', 'c']

Aplicaciones reales

🖨️ Cola de impresión

Los trabajos de impresión se procesan en el orden en que llegan.

💻 Planificación de tareas

Los sistemas operativos usan colas para gestionar procesos y ejecución de tareas.

🔍 Algoritmo BFS

La búsqueda en anchura usa una cola para explorar nodos nivel por nivel.

📞 Centro de llamadas

Las llamadas se atienden en el orden en que se reciben.

🎮 Sistema de turnos en juegos

Los juegos multijugador usan colas para gestionar turnos y acciones.

📨 Cola de mensajes

Sistemas de mensajería asíncrona (RabbitMQ, Kafka) usan colas.

Comparación cola vs pila

Aspecto Cola (FIFO) Pila (LIFO)
Principio Primero en entrar, primero en salir Último en entrar, primero en salir
Insertar/Eliminar Insertar al final, eliminar del frente Ambos en la parte superior
Operaciones enqueue(), dequeue() push(), pop()
Uso típico BFS, planificación de tareas DFS, llamadas de funciones, undo
Mundo real Fila de espera Pila de platos

💡 Puntos clave

  • ✓ La cola sigue el principio FIFO (First In, First Out)
  • ✓ Todas las operaciones son O(1) con una implementación adecuada
  • ✓ Usa lista enlazada u objeto optimizado para dequeue O(1)
  • ✓ Las colas circulares usan eficientemente arrays de tamaño fijo
  • ✓ Las colas de prioridad ordenan por prioridad, no por llegada
  • ✓ Esencial para BFS, planificación de tareas y mensajería