Las colas son estructuras de datos lineales que atienden elementos en el orden exacto en el que llegan. En C se modelan como un TDA compuesto por posiciones contiguas o nodos enlazados que exponen operaciones acotadas para insertar y extraer sin romper los invariantes del frente.
A lo largo del tutorial exploramos el modelo FIFO, revisamos colas simples, circulares y dobles, y explicamos cuándo conviene derivar hacia una cola de prioridad respaldada por un heap para asignar turnos según importancia.
Esta visión general también conecta con la implementación de colas de tareas, productores/consumidores y planificadores que veremos en los temas siguientes.
Una cola mantiene un puntero front (frente) que apunta al próximo elemento a retirar y un puntero rear (final) donde se inserta la siguiente solicitud. Al igual que cualquier TDA, define una capacidad y estados especiales para cola vacía o llena, lo que obliga a validar condiciones antes de mover punteros.
Los punteros avanzan en sentidos complementarios para garantizar el orden FIFO.
El siguiente fragmento muestra una implementación fija basada en array que deja listos los auxiliares de capacidad y los accesos a las operaciones principales:
#define TAM_MAX 128
typedef struct {
int datos[TAM_MAX];
int frente;
int final;
int cantidad;
} Cola;
void inicializar(Cola *cola) {
cola->frente = 0;
cola->final = 0;
cola->cantidad = 0;
}
int enqueue(Cola *cola, int valor) {
if (cola->cantidad == TAM_MAX) {
return 0;
}
cola->datos[cola->final] = valor;
cola->final = (cola->final + 1) % TAM_MAX;
cola->cantidad++;
return 1;
}
int dequeue(Cola *cola, int *valor) {
if (cola->cantidad == 0) {
return 0;
}
*valor = cola->datos[cola->frente];
cola->frente = (cola->frente + 1) % TAM_MAX;
cola->cantidad--;
return 1;
}
int peek(const Cola *cola) {
return cola->cantidad == 0 ? -1 : cola->datos[cola->frente];
}
La función enqueue valida el estado de cola llena, mientras que dequeue evita leer fuera de rango cuando no hay elementos pendientes. Las posiciones se ciclan con el operador módulo para reutilizar el arreglo sin desplazar datos.
Para ver la cola en acción, basta con un main que simule el ingreso de eventos y reporte qué elemento se encuentra en la cabecera:
#include <stdio.h>
int main(void) {
Cola cola;
inicializar(&cola);
enqueue(&cola, 5);
enqueue(&cola, 8);
enqueue(&cola, 13);
int valor;
while (dequeue(&cola, &valor)) {
printf("Atendido: %d\n", valor);
}
return 0;
}
Puedes copiar ambos fragmentos en CLion o en tu entorno favorito para observar cómo se respetan los estados vacío/lleno y para instrumentar pruebas con aserciones.
FIFO describe la regla principal de una cola: el primer elemento que ingresa debe salir antes que los demás. Se implementa controlando los índices frente y final sin recorrer posiciones intermedias.
frente apunta al 12 y final se desplaza tres posiciones.dequeue entrega 12 y actualiza frente a 18 sin tocar el resto de la memoria.Este modelo mantiene la predictibilidad en colas de impresión, colas de mensajes o buffers de red. Solo se altera en escenarios de prioridad, donde la ordenación se delega a una estructura auxiliar.
Aunque pilas y colas son lineales, se utilizan para objetivos distintos. La comparación siguiente resume los contrastes más relevantes:
| Característica | Cola lineal | Pila | Cola de prioridad |
|---|---|---|---|
| Orden de servicio | FIFO estricto | LIFO (el último en entrar sale primero) | Depende de la prioridad asignada |
| Extremos usados | Frente para extraer, final para insertar | Un solo tope | Frente virtual definido por el valor más prioritario |
| Complejidad | O(1) encolado y desencolado | O(1) push/pop | O(log n) al insertar o extraer si se usa un heap |
| Casos de uso | Turnos, buffers, tareas pendientes | Deshacer/rehacer, evaluación de expresiones | Planificadores, sistemas de tickets VIP, Dijkstra |
Visualizar estos comportamientos ayuda a diseñar APIs claras para las colas específicas que abordaremos en los siguientes temas.