Colas y Filas de Prioridad: Funcionamiento y Aplicaciones
Resumen
¿Qué son las colas y cómo funcionan?
Las estructuras de datos juegan un papel fundamental en la programación, y uno de los tipos más comunes es la cola, o queue en inglés. Imagina estar en una fila en el supermercado o esperando para abordar un avión; estas experiencias cotidianas son ejemplos de cómo funcionan las colas en la programación. En las colas, se sigue el principio del FIFO (First In, First Out), es decir, el primero que entra es el primero que sale. Vamos a explorar cómo funcionan y sus variantes.
¿Cómo se organizan las colas?
FIFO (First In, First Out): Cada elemento que entra en la cola sale en el mismo orden en que llegó. Esto se ve con el ejemplo del supermercado, donde el primer cliente en llegar es el primero en ser atendido.
Rear y Front:
Rear: Es el último elemento en entrar. Imagina que es como el último cliente que se une a la fila.
Front: Es el primer elemento en salir o ser atendido. En términos de programación, cuando el elemento front es procesado, el siguiente en la fila se convierte en el nuevo front.
¿Qué son las colas de prioridad?
Además de las colas tradicionales, existen las Priority Queues que permiten asignar prioridades a los elementos dentro de la cola. Esto significa que, aunque se siga un orden FIFO, ciertos elementos pueden ser atendidos antes si tienen una prioridad más alta.
Ejemplos de colas de prioridad incluyen:
Abordaje de aviones: Pasajeros con pases ejecutivos o de primera clase pueden abordar antes que otros.
Hospitales: Pacientes con condiciones más urgentes son atendidos antes, independientemente de cuándo llegaron.
Bancos: Clientes preferenciales con ciertos productos financieros pueden ser atendidos antes que otros.
¿Cuáles son los métodos comunes de las colas?
Las colas tienen varios métodos que permiten su gestión eficiente. Algunos de los más comunes incluyen:
Verificar si está vacía: Método para comprobar si la cola no contiene elementos.
Obtener tamaño o longitud: Saber cuántos elementos hay en la cola en un momento determinado.
Mostrar contenido: Representar la cola como un string para visualizarla.
Generar iteradores: Facilitan recorrer los elementos de la cola.
Método de membresía: Verificar si un elemento específico está presente en la cola.
Combinar colas: Integrar una cola dentro de otra, similar a cuando dos filas en un supermercado se fusionan.
Eliminar todos los elementos: Vaciar la cola por completo.
¿Cómo puedo implementar colas en programación?
La versatilidad de las colas permite adaptarlas a necesidades específicas al diseñarlas. Para implementar colas en código, se utilizan distintos métodos y estructuras que puedes personalizar según el contexto de tu programa o aplicación.
En resumen, las colas son una poderosa herramienta en programación para manejar datos de manera eficiente y lógica. ¡No dudes en experimentar con ellas y adaptarlas a tus proyectos!
En los métodos del queue hay errores en la descripción del método add y pop, lo que dice describe lo que hacen esos métodos en un Stack.
lo explico al revés no ???
Queue con Nodos
Me adelanto un poco y les comparto mi implementación de los métodos básicos que mencionó en la clase.
Código
from typing import Any
from typing import Iterable
from typing import Optional
from node import Node
classQueue:"""Represents a simple queue.""" _front: Optional[Node] _rear: Optional[Node] _size:intdef__init__(self,*items)->None:"""Initializes the Queue""" self._front =None self._rear =None self._size =0iflen(items)>0andisinstance(items[0], Iterable): items = items[0]for item in items: self.add(item)defclear(self):"""Clears the queue.
Time complexity: O(1)
""" self._front =None self._rear =None self._size =0defpeek(self):"""Returns the first element of the queue.
Raises IndexError if the queue is empty.
Time complexity: O(1)
Returns:
Any: First element of the queue.
"""if self._front isNone:raise IndexError('The queue is empty.')return self._front.value
defis_empty(self)->bool:"""Check if the queue is empty.
Returns:
bool: True if the queue is empty. False otherwise.
"""return self._front isNonedefadd(self, value: Any)->None:"""Adds a new value to the queue.
Time Complexity: O(1)
Args:
value (Any): Value to be added.
Returns:
None
"""if self._front isNone: self._front = Node(value) self._rear = self._front
else: self._rear.next= Node(value) self._rear = self._rear.next self._size +=1returnNonedefpop(self):"""Removes a value from the queue.
Raises IndexError if the queue is empty.
Time complexity: O(1)
Returns:
Any: Removed value.
"""if self._front isNone:raise IndexError('The queue is empty') value = self._front.value
self._front = self._front.next self._size -=1return value
defcopy(self):"""Returns a copy of the current queue.
Time complexity: O(1)
Returns:
Queue: Copy of the current queue.
""" new_queue = Queue() new_queue._front = self._front
new_queue._rear = self._rear
new_queue._size = self._size
return new_queue
defdepthcopy(self):"""Returns a copy of the current queue.
This function will create a copy of each node in the queue.
Time Complexity: O(n)
Returns:
Queue: Copy of the queue.
""" new_queue = Queue() pointer = self._front
while pointer isnotNone: new_queue.add(pointer.value) pointer = pointer.nextreturn new_queue
defitervalues(self)-> Iterable[Any]:"""Generates an iterable of the values in the queue.
Time complexity: O(n)
Returns:
Iterable: Iterable of the values in the queue.
""" pointer = self._front
while pointer isnotNone:yield pointer.value
pointer = pointer.nextreturnNonedefiternodes(self)-> Iterable[Node]:"""Generates an iterable of the nodes in the queue.
Returns:
Iterable: Iterable of the nodes in the queue.
""" pointer = self._front
while pointer isnotNone:yield pointer
pointer = pointer.nextreturnNonedef__len__(self)->int:"""Returns the length of the queue when using the `len` built-in function.
Time complexity: O(1)
Returns:
int: Number of nodes into the queue.
"""return self._size
def__str__(self):"""Returns a string representation of the queue.
Time complexity: O(n)
Args:
str: Representation of the queue
"""return'->'.join(self.itervalues())def__iter__(self)-> Iterable[Any]:"""Generates an iterable of the values in the queue.
Time complexity: O(n)
Returns:
Iterable: Iterable of the values in the queue.
"""return self.itervalues()def__contains__(self, value: Any)->bool:"""Checks if a value is in the queue.
Time complexity: O(n)
Args:
value: Value to be searched in the queue.
Returns:
bool: True if the value is in the queue. False otherwise.
"""for item in self:if item == value:returnTruereturnFalsedef__add__(self, other: Iterable)->'Queue':"""Returns a new queue with the items of both queues.
Time complexity: O(n)
Args:
other (Iterable): Items to be added to the current queue.
Returns:
Queue: New queue with the items of both queues.
""" new_queue = self.depthcopy()for item in other: new_queue.add(item)return new_queue
Unittest
# Unittestimport unittest
# Utilitiesfrom queue import Queue
classQueueTestCase(unittest.TestCase):"""Queue test cases."""deftest_initialize_empty(self):"""Test initialize empty queue.""" _queue = Queue() self.assertEqual(len(_queue),0)deftest_intialize_with_list(self):"""Test initialize with list.""" _queue = Queue([1,2,3]) self.assertEqual(len(_queue),3)for a, b inzip(_queue,[1,2,3]): self.assertEqual(a, b)deftest_initialize_with_tuple(self):"""Test initialize with tuple.""" _queue = Queue((1,2,3)) self.assertEqual(len(_queue),3)for a, b inzip(_queue,[1,2,3]): self.assertEqual(a, b)deftest_initialize_with_set(self):"""Test initialize with set.""" _queue = Queue({1,2,3}) self.assertEqual(len(_queue),3)for a, b inzip(_queue,[1,2,3]): self.assertEqual(a, b)deftest_initialize_with_iterable(self):"""Test initialize with iterable.""" _queue = Queue(range(1,4)) self.assertEqual(len(_queue),3)for a, b inzip(_queue,[1,2,3]): self.assertEqual(a, b)deftest_is_empty(self):"""Test is empty.""" _queue = Queue() self.assertTrue(_queue.is_empty()) _queue = Queue([1,2,3]) self.assertFalse(_queue.is_empty())deftest_add(self):"""Test add.""" _queue = Queue() _queue.add(1) self.assertEqual(len(_queue),1) _queue.add(2) self.assertEqual(len(_queue),2)deftest_peek(self):"""Test peek.""" _queue = Queue() self.assertRaises(IndexError, _queue.peek) _queue = Queue([1,2,3]) self.assertEqual(_queue.peek(),1)deftest_pop(self):"""Test pop.""" _queue = Queue([1,2,3]) self.assertEqual(_queue.pop(),1) self.assertEqual(_queue.pop(),2) self.assertEqual(_queue.pop(),3) self.assertRaises(IndexError, _queue.pop)deftest_copy(self):"""Test copy.""" _queue = Queue([1,2,3]) queue_copy = _queue.copy() self.assertIsNot(_queue, queue_copy) self.assertEqual(len(_queue),len(queue_copy))for a, b inzip(_queue.iternodes(), queue_copy.iternodes()): self.assertEqual(a, b) self.assertIs(a, b)deftest_depthcopy(self):"""Test depthcopy.""" _queue = Queue([1,2,3]) queue_copy: Queue = _queue.depthcopy() self.assertIsNot(_queue, queue_copy) self.assertEqual(len(_queue),len(queue_copy))for a, b inzip(_queue.iternodes(), queue_copy.iternodes()): self.assertEqual(a, b) self.assertIsNot(a, b)deftest_clear(self):"""Test clear.""" _queue = Queue([1,2,3]) _queue.clear() self.assertEqual(len(_queue),0)deftest_contains(self):"""Test contains.""" _queue = Queue([1,2,3]) self.assertTrue(1in _queue) self.assertFalse(4in _queue)deftest_add_operator(self):"""Test add operator.""" _queue = Queue([1,2,3]) _queue +=[4,5,6] self.assertEqual(len(_queue),6)for a, b inzip(_queue,[1,2,3,4,5,6]): self.assertEqual(a, b)
se ve el código muy limpio, como usas las comprehentios, como usas los métodos privados con _, etc
Yo creo que lo que falto en este curso fue un Proyecto desde el Inicio, como en un curso previo se hizo PlatziBlog y se fue Modificando conforme iba enseñando cosas nuevas.
Lo que entiendo es que con estructuras de datos mas simples se puede hacer todo lo que el explico pero mil veces mas simple, sin embargo con mas consumo de recursos, considero que si creaba un proyecto con lo que ya conociamos y lo iba modificando con estas nuevas estructuras y mostraba la diferencia entre ambos todo hubiera quedado muchisimo mas claro.
Me sorprende la cantidad de gente que dejo el curso por el módulo anterior 😅 los entiendo, todo lo hice entendí fue apunta de IA.
Asi es como me ha tocado
Por si alguno no nota el mensaje subliminar de los videos... En todos hay una voz que susurra "platzi"
Puedes dar el minuto? para comprobarlo jaja.
Justo en la transición del inicio, escucha atentamente mientras suena la música y el logo pasa
Les dejo el código comnetado paso a paso para mayor entendimiento:
from node importNode # Importa la clase Node desde el archivo node.pyclassStack: def __init__(self): self.top=None # Inicializa el tope de la pila como None self.size=0 # Inicializa el tamaño de la pila como 0 def push(self, data): # Crea un nuevo nodo con el dato y apunta al nodo actual en el tope
node =Node(data, self.top)if self.top: # Si la pila no está vacía
node.next= self.top # El siguiente del nuevo nodo es el nodo actual en el tope
self.top= node # El tope ahora es el nuevo nodo
else: self.top= node # Si la pila está vacía, el nuevo nodo es el tope
self.size+=1 # Incrementa el tamaño de la pila
def pop(self):if self.top: # Si la pila no está vacía
data = self.top.data # Obtiene el dato del nodo en el tope
self.size-=1 # Reduce el tamaño de la pila
if self.top.next: # Si hay un nodo siguiente al nodo actual en el tope
self.top= self.top.next # El nuevo tope es el nodo siguiente
else: self.top=None # Si no hay nodo siguiente, la pila está vacía
return data # Retorna el dato del nodo eliminado
else:return("The stack is empty") # Retorna un mensaje si la pila está vacía
def peek(self):return self.top.dataif self.topelse("The stack is empty") # Retorna el dato en el tope de la pila si la pila no está vacía, de lo contrario, retorna un mensaje
def clear(self):while self.top: # Mientras la pila no esté vacía
self.pop() # Elimina el nodo en el tope de la pila
def search_element(self, data):if self.top: # Si la pila no está vacía
current = self.top # Comienza desde el nodo en el tope
whilecurrent: # Mientras haya un nodo actual
if current.data== data: # Si el dato del nodo actual es igual al dato buscado
return(f'Elemet {data} founded') # Retorna un mensaje indicando que el elemento fue encontrado
current = current.next # Avanza al siguiente nodo
return(f'Elemet {data} not founded') # Retorna un mensaje indicando que el elemento no fue encontrado
```Les dejo el código comnetado paso a paso para mayor entendimiento:
Les dejo el código comnetado paso a paso para mayor entendimiento:
from node import Node # Importa la clase Node desde el archivo node.py
class Stack:
def __init__(self):
self.top = None # Inicializa el tope de la pila como None
self.size = 0 # Inicializa el tamaño de la pila como 0
def push(self, data):
# Crea un nuevo nodo con el dato y apunta al nodo actual en el tope
node = Node(data, self.top)
if self.top: # Si la pila no está vacía
node.next = self.top # El siguiente del nuevo nodo es el nodo actual en el tope
self.top = node # El tope ahora es el nuevo nodo
else:
self.top = node # Si la pila está vacía, el nuevo nodo es el tope
self.size += 1 # Incrementa el tamaño de la pila
def pop(self):
if self.top: # Si la pila no está vacía
data = self.top.data # Obtiene el dato del nodo en el tope
self.size -= 1 # Reduce el tamaño de la pila
if self.top.next: # Si hay un nodo siguiente al nodo actual en el tope
self.top = self.top.next # El nuevo tope es el nodo siguiente
else:
self.top = None # Si no hay nodo siguiente, la pila está vacía
return data # Retorna el dato del nodo eliminado
else:
return ("The stack is empty") # Retorna un mensaje si la pila está vacía
def peek(self):
return self.top.data if self.top else ("The stack is empty") # Retorna el dato en el tope de la pila si la pila no está vacía, de lo contrario, retorna un mensaje
def clear(self):
while self.top: # Mientras la pila no esté vacía
self.pop() # Elimina el nodo en el tope de la pila
def search_element(self, data):
if self.top: # Si la pila no está vacía
current = self.top # Comienza desde el nodo en el tope
while current: # Mientras haya un nodo actual
if current.data == data: # Si el dato del nodo actual es igual al dato buscado
return (f'Elemet {data} founded') # Retorna un mensaje indicando que el elemento fue encontrado
current = current.next # Avanza al siguiente nodo
return (f'Elemet {data} not founded') # Retorna un mensaje indicando que el elemento no fue encontrado
Las queues, también conocidas como colas, son una estructura de datos lineal que sigue el principio de FIFO (First In, First Out), lo que significa que el primer elemento en entrar es el primero en ser atendido o procesado
.
En una cola, los elementos se agregan al final y se eliminan desde el frente. Esto se asemeja a una fila en la vida real, donde la primera persona en llegar es la primera en ser atendida.
Las operaciones principales en una cola son:
Enqueue (encolar):
Agrega un elemento al final de la cola.
Dequeue (desencolar):
Elimina y devuelve el elemento que está en el frente de la cola.
Front (frente):
Devuelve el elemento que está en el frente de la cola sin eliminarlo.
Rear (trasero):
Devuelve el elemento que está al final de la cola sin modificar la cola.
Is Empty (está vacía):
Verifica si la cola está vacía o no.
Las queues se utilizan comúnmente en situaciones donde es necesario mantener un orden de llegada y procesamiento, como:
Procesamiento de tareas en un sistema operativo.Implementación de buffers en comunicaciones de red.Planificación de tareas en algoritmos de búsqueda y exploración.Control de flujo de datos en aplicaciones que manejan eventos y procesos en serie.A continuación, se muestra un ejemplo simple de implementación de una cola en Python utilizando una lista: