Introducción a las Colas de Prioridad

Las colas de prioridad son una extensión de las colas estándar en las que cada elemento tiene una "prioridad" asociada. En lugar de seguir el orden de llegada (FIFO), los elementos se extraen de acuerdo con su prioridad. Las colas de prioridad son fundamentales en muchos algoritmos y aplicaciones, como la planificación de procesos en sistemas operativos y la gestión de eventos en simulaciones.

Conceptos Clave

  1. Prioridad: Cada elemento en la cola tiene una prioridad. Los elementos con mayor prioridad se procesan antes que los de menor prioridad.
  2. Operaciones Básicas:
    • Insertar (Enqueue): Añadir un elemento a la cola con una prioridad específica.
    • Eliminar (Dequeue): Extraer el elemento con la mayor prioridad.
  3. Implementaciones Comunes:
    • Montículos (Heaps): Estructuras de datos especializadas que permiten una eficiente inserción y extracción de elementos con prioridad.
    • Listas Ordenadas: Listas en las que los elementos se mantienen ordenados según su prioridad.
    • Listas No Ordenadas: Listas en las que los elementos se insertan en cualquier posición, y la búsqueda del elemento con mayor prioridad se realiza al extraer.

Implementación de Colas de Prioridad

Usando Montículos (Heaps)

Los montículos son estructuras de datos en forma de árbol binario que permiten una eficiente inserción y extracción de elementos con prioridad. Existen dos tipos principales de montículos:

  • Montículo Máximo (Max-Heap): El nodo padre siempre tiene un valor mayor o igual que sus hijos.
  • Montículo Mínimo (Min-Heap): El nodo padre siempre tiene un valor menor o igual que sus hijos.

Ejemplo en Python: Montículo Mínimo

import heapq

class ColaDePrioridad:
    def __init__(self):
        self.heap = []

    def insertar(self, elemento, prioridad):
        heapq.heappush(self.heap, (prioridad, elemento))

    def eliminar(self):
        if not self.heap:
            raise IndexError("La cola de prioridad está vacía")
        return heapq.heappop(self.heap)[1]

    def esta_vacia(self):
        return len(self.heap) == 0

# Ejemplo de uso
cola = ColaDePrioridad()
cola.insertar("tarea1", 2)
cola.insertar("tarea2", 1)
cola.insertar("tarea3", 3)

while not cola.esta_vacia():
    print(cola.eliminar())

Explicación del Código:

  • heapq.heappush(self.heap, (prioridad, elemento)): Inserta un elemento en el montículo con su prioridad.
  • heapq.heappop(self.heap): Extrae el elemento con la menor prioridad (en un Min-Heap).
  • esta_vacia(): Verifica si la cola de prioridad está vacía.

Usando Listas Ordenadas

En una lista ordenada, los elementos se insertan en la posición correcta según su prioridad, lo que permite una extracción rápida del elemento con mayor prioridad.

Ejemplo en Python: Lista Ordenada

class ColaDePrioridadOrdenada:
    def __init__(self):
        self.lista = []

    def insertar(self, elemento, prioridad):
        self.lista.append((prioridad, elemento))
        self.lista.sort(key=lambda x: x[0])

    def eliminar(self):
        if not self.lista:
            raise IndexError("La cola de prioridad está vacía")
        return self.lista.pop(0)[1]

    def esta_vacia(self):
        return len(self.lista) == 0

# Ejemplo de uso
cola = ColaDePrioridadOrdenada()
cola.insertar("tarea1", 2)
cola.insertar("tarea2", 1)
cola.insertar("tarea3", 3)

while not cola.esta_vacia():
    print(cola.eliminar())

Explicación del Código:

  • self.lista.sort(key=lambda x: x[0]): Ordena la lista según la prioridad después de cada inserción.
  • self.lista.pop(0): Extrae el primer elemento de la lista, que tiene la menor prioridad.

Ejercicios con Colas de Prioridad

Ejercicio 1: Implementar una Cola de Prioridad usando un Montículo Máximo

Descripción: Implementa una clase ColaDePrioridadMax que utilice un montículo máximo para gestionar las prioridades.

Código Base:

import heapq

class ColaDePrioridadMax:
    def __init__(self):
        self.heap = []

    def insertar(self, elemento, prioridad):
        # Completa esta función
        pass

    def eliminar(self):
        # Completa esta función
        pass

    def esta_vacia(self):
        return len(self.heap) == 0

# Ejemplo de uso
cola = ColaDePrioridadMax()
cola.insertar("tarea1", 2)
cola.insertar("tarea2", 1)
cola.insertar("tarea3", 3)

while not cola.esta_vacia():
    print(cola.eliminar())

Solución:

import heapq

class ColaDePrioridadMax:
    def __init__(self):
        self.heap = []

    def insertar(self, elemento, prioridad):
        heapq.heappush(self.heap, (-prioridad, elemento))

    def eliminar(self):
        if not self.heap:
            raise IndexError("La cola de prioridad está vacía")
        return heapq.heappop(self.heap)[1]

    def esta_vacia(self):
        return len(self.heap) == 0

# Ejemplo de uso
cola = ColaDePrioridadMax()
cola.insertar("tarea1", 2)
cola.insertar("tarea2", 1)
cola.insertar("tarea3", 3)

while not cola.esta_vacia():
    print(cola.eliminar())

Explicación del Código:

  • heapq.heappush(self.heap, (-prioridad, elemento)): Inserta el elemento con prioridad negativa para simular un montículo máximo.
  • heapq.heappop(self.heap): Extrae el elemento con la mayor prioridad (debido a la prioridad negativa).

Ejercicio 2: Simulación de un Sistema de Gestión de Tareas

Descripción: Implementa un sistema de gestión de tareas donde cada tarea tiene una prioridad. Usa una cola de prioridad para gestionar las tareas.

Código Base:

class SistemaDeGestionDeTareas:
    def __init__(self):
        self.cola = ColaDePrioridad()

    def agregar_tarea(self, tarea, prioridad):
        # Completa esta función
        pass

    def procesar_tareas(self):
        # Completa esta función
        pass

# Ejemplo de uso
sistema = SistemaDeGestionDeTareas()
sistema.agregar_tarea("tarea1", 2)
sistema.agregar_tarea("tarea2", 1)
sistema.agregar_tarea("tarea3", 3)
sistema.procesar_tareas()

Solución:

class SistemaDeGestionDeTareas:
    def __init__(self):
        self.cola = ColaDePrioridad()

    def agregar_tarea(self, tarea, prioridad):
        self.cola.insertar(tarea, prioridad)

    def procesar_tareas(self):
        while not self.cola.esta_vacia():
            tarea = self.cola.eliminar()
            print(f"Procesando {tarea}")

# Ejemplo de uso
sistema = SistemaDeGestionDeTareas()
sistema.agregar_tarea("tarea1", 2)
sistema.agregar_tarea("tarea2", 1)
sistema.agregar_tarea("tarea3", 3)
sistema.procesar_tareas()

Explicación del Código:

  • agregar_tarea(self, tarea, prioridad): Añade una tarea a la cola de prioridad.
  • procesar_tareas(self): Procesa y elimina las tareas de la cola según su prioridad.

Conclusión

En esta sección, hemos explorado las colas de prioridad, su importancia y diferentes métodos de implementación. Las colas de prioridad son esenciales en muchas aplicaciones donde el orden de procesamiento depende de la importancia de los elementos. Hemos cubierto implementaciones usando montículos y listas ordenadas, y proporcionado ejercicios prácticos para reforzar el aprendizaje.

En la siguiente sección, profundizaremos en los árboles, otra estructura de datos fundamental en la programación.

© Copyright 2024. Todos los derechos reservados