En el fascinante mundo de las estructuras de datos, los queues (colas) pueden construirse sobre elementos simples como listas, arrays o incluso nodos. En esta clase, exploramos cómo crear un queue basado en nodos, específicamente utilizando una lista doblemente enlazada. Este enfoque optimiza ciertas operaciones, ofreciendo una eficiencia en tiempo constante, un tema conocido como notación Big O.
¿Qué es un nodo y cómo se utiliza en un Queue?
Un nodo es una estructura que lleva un valor y referencias al siguiente y anterior nodo en la estructura. Imagina un vagón de tren; cada nodo contiene datos y se conecta con el anterior y el siguiente. La gran ventaja de usar nodos, en lugar de otras estructuras como arrays, es que agregar o eliminar nodos es una operación muy eficiente, siendo de orden constante O(1) en notación Big O.
Implementación de la clase Queue
Vamos a implementar la clase Queue utilizando nodos como base. Primero, necesitas crear la clase para representar la cola y luego implementar los métodos esenciales: enqueue (añadir elementos) y dequeue (eliminar elementos).
A continuación, vemos cómo implementar esta clase:
Ahora que tenemos los métodos básicos implementados en nuestra clase Queue, es hora de ponerlos a prueba en un entorno interactivo de Python:
Importa la clase Queue.
Crea una instancia.
Añade elementos utilizando el método enqueue.
Remueve elementos utilizando dequeue.
# Importar la clase Queue desde el módulo donde está implementadafrom your_module import Queue
# Instanciar la colami_cola = Queue()# Añadir elementosmi_cola.enqueue("huevos")mi_cola.enqueue("jamón")mi_cola.enqueue("spa")# Verificar el primer elementoprint(mi_cola.head.data)# Salida esperada: "huevos"# Remover elementosprint(mi_cola.dequeue())# Salida esperada: "huevos"
Aplicaciones prácticas de un Queue basado en nodos
Un queue basado en nodos es especialmente útil en sistemas donde el manejo eficiente y dinámico de datos es crucial. Piensa en un sistema de gestión de turnos: puedes tener una base de datos de clientes o pacientes, donde un queue asegura que se atienden en el orden de llegada sin necesidad de duplicar datos en otras estructuras como arrays, que pueden tener limitaciones de tamaño.
Este tipo de estructura también se utiliza en simulaciones de playlists musicales, donde las canciones, organizadas como nodos, se tocan secuencialmente.
Más allá de lo básico
Hemos explorado cómo la integración de métodos y estructuras puede llevar a una implementación más eficiente. Pero esto es solo el comienzo. Te animo a continuar explorando y modificando esta estructura para adecuarla a tus necesidades específicas. La programación se trata de adaptabilidad y experimentación, así que ¡sigue explorando!
from node_based_queue importQueuefood =Queue()food.enqueue('eggs')food.enqueue('ham')food.enqueue('spam')print(food.head.data)print(food.head.next.data)print(food.tail.data)print(food.tail.previous.data)print(food.count)print(food.dequeue())print(food.head.data)
eggs
ham
spam
ham
3eggs
ham
Muchas gracias por tu aporte.
No pensé que fuéramos a ver aquí Big O, pero a la par del curso voy leyendo un libro llamado Data Structure and Algorithmic Thinking with Python y al principio viene esta bella tabla.
Muchas gracias por tu aporte.
La construcción de queue basadas en nodos tiene una medida de O(1) ya que trabaja unicamente con if, elif y else, mientras que la construcción de un queue basado en un stack, implica un ciclo while, por lo tanto tiene una medida de O(n).
Hola profesor, excelente video. Ya que veo la misma situación en algunos videos anteriores, quería preguntarle si el poner "object" en class TwoWayNode(object) algoritmicamente es igual a no colocarlo? o tiene alguna ventaja.
saludos
Hola Elvis!!
Es exactamente lo mismo, "object" es el valor por defecto cuando no se le asigna ninguna clase padre a la clase que estoy codificando.
Gracias por transcribir el código y narrarlo.
como se llama el plugin en vs code, que le da colores a cada funcion ?
Holaa :)
Por defecto el editor de código le da colores 🤔
O nos puedes pasar captura de a qué te refieres exactamente?
Hola a estas lineas de color:
mi codigo
classQueue: def __init__(self): self.head=None self.tail=None self.count=0 def enqueue(self, data): new_node =Double_node(data)if self.count==0: self.head= new_node
self.tail= new_node
self.count+=1else: self.tail.prev= new_node
new_node.next= self.tail self.tail= new_node
self.count+=1return def dequeue(self):if self.count==0:return'no hay elementos en la queue' elif self.count==1: data = self.head.data self.head=None self.tail=None self.count-=1return data
else: value = self.head.data self.head= self.head.prev self.head.next=None self.count-=1return value
Muchas gracias por tu aporte.
Dejo el código de la clase comentado:
classTwoWayNode(object): def __init__(self, data=None, next=None, previous=None): self.data= data # Datos almacenados en el nodo
self.next= next # Referencia al siguiente nodo
self.previous= previous # Referencia al nodo anterior(en una estructura de doble enlace)classQueue: def __init__(self): self.head=None # Nodo al principio de la cola
self.tail=None # Nodo al final de la cola
self.count=0 # Contador de elementos en la cola
def enqueue(self, data): new_node =TwoWayNode(data,None,None) # Crea un nuevo nodo con los datos
if self.head is None: # Si la cola está vacía
self.head= new_node # El nuevo nodo se convierte en la cabeza y la cola
self.tail= self.headelse: # Si la cola no está vacía
new_node.previous= self.tail # El nodo anterior al nuevo nodo es el nodo actual de la cola
self.tail.next= new_node # El siguiente del nodo actual de la cola es el nuevo nodo
self.tail= new_node # El nuevo nodo se convierte en la nueva cola
self.count+=1 # Incrementa el contador de elementos en la cola
def dequeue(self): current = self.head # Obtiene el nodo actual al principio de la cola
if self.count==1: # Si hay un solo elemento en la cola
self.count-=1 # Decrementa el contador de elementos
self.head=None # Resetea la cabeza y la cola a None self.tail=None elif self.count>1: # Si hay más de un elemento en la cola
self.head= self.head.next # El siguiente nodo al actual se convierte en el nuevo nodo de la cabeza
self.head.previous=None # Resetea la referencia al nodo anterior de la cabeza a None self.count-=1 # Decrementa el contador de elementos
return current.data # Retorna los datos del nodo actual(el que se eliminó de la cola)```classTwoWayNode(object):  def \_\_init\_\_(self, data=None, next=None, previous=None):  self.data= data # Datos almacenados en el nodo
  self.next= next # Referencia al siguiente nodo
  self.previous= previous # Referencia al nodo anterior(en una estructura de doble enlace)classQueue:  def \_\_init\_\_(self):  self.head=None # Nodo al principio de la cola
  self.tail=None # Nodo al final de la cola
  self.count=0 # Contador de elementos en la cola
  def enqueue(self, data): new\_node =TwoWayNode(data,None,None) # Crea un nuevo nodo con los datos
 if self.head is None: # Si la cola está vacía
  self.head=new\_node # El nuevo nodo se convierte en la cabeza y la cola
  self.tail= self.head else: # Si la cola no está vacía
 new\_node.previous= self.tail # El nodo anterior al nuevo nodo es el nodo actual de la cola
  self.tail.next=new\_node # El siguiente del nodo actual de la cola es el nuevo nodo
  self.tail=new\_node # El nuevo nodo se convierte en la nueva cola
  self.count+=1 # Incrementa el contador de elementos en la cola
  def dequeue(self):  current = self.head # Obtiene el nodo actual al principio de la cola
 if self.count==1: # Si hay un solo elemento en la cola
  self.count-=1 # Decrementa el contador de elementos
  self.head=None # Resetea la cabeza y la cola a None  self.tail=None  elif self.count>1: # Si hay más de un elemento en la cola
  self.head= self.head.next # El siguiente nodo al actual se convierte en el nuevo nodo de la cabeza
  self.head.previous=None # Resetea la referencia al nodo anterior de la cabeza a None  self.count-=1 # Decrementa el contador de elementos
 return current.data # Retorna los datos del nodo actual(el que se eliminó de la cola)
¡Hola! Les comparto el código que realice antes de tomar la clase. Puede que genere un poco de valor para ti, además si encuentras algún área de oportunidad (Soy consciente de que los nombres de los métodos deberían ser otros) no dudes en darla a conocer.
_**Acercamiento: **_Teniendo en cuenta que un queue requiere almacenar el primer valor recibido (Front/Head) y un último valor recibido (Rear/Tail), decidí estructuras los nodos de izquierda a derecha, siendo el valor de la izquierda el primer elemento que se recibió, y, el de la derecha el último. Por lo tanto, el valor que tendría que actualizar cada vez que se agregue un nuevo nodo sería rear/tail, dejando front/head quieto ++a menos que no haya ningun elemento en estos.++ Por el contrario, cuando deseemos eliminar el nodo en front/head pues aplicaríamos lo inverso al método de agregar nodos, donde front/head es el valor a actualizar, y, el rear/tail quieto.
Código:
from typing importAnyclassNode: def __init__(self,data:Any): self.data:Any|None= data
self.next:Any|None=None self.previous:Any|None=NoneclassQueue: def __init__(self):"""
self.front should be the First-In and the First-Out.self.rear should represent last element in the queue."""
self.front=None self.rear=None def push(self,data:Any)->None:"""
This method will allow to add newnodes to the linked list.Args:data:AnyReturns:None"""
# Instanceof the Nodeclassbased on the data given.node=Node(data) # If nothing exist in front of the queue, then...if self.front==None: self.front= node
self.rear= self.frontelse: temporal = self.rear temporal.next= node
self.rear= temporal.next self.rear.previous= temporal
def pop_element(self):if self.front: self.front= self.front.nextelse:print("Stack is already empty...") def show_queue(self): iterator = self.frontwhileiterator:print(f"{iterator.data} -> ", end="") iterator = iterator.next # print(f" <- {self.rear.data}")if __name__ =="__main__": macdonalds =Queue() macdonalds.push("Big mac") macdonalds.push("French fries") macdonalds.push("Ice cream") macdonalds.push("Mashed potatoes") macdonalds.pop_element() macdonalds.pop_element()"""
macdonalds.pop_element() macdonalds.pop_element() macdonalds.pop_element()"""
macdonalds.show_queue()
Por último, realicé un pequeño dibujo en Paint para entender mejor como debía realizar la implementación:
Yo le hubiese llamado al archivo node_based_twowaynode.py
Codigo de la Clase
classTwoWayNode(object): def __init__(self, data =None, next =None, previous =None): self.data= data
self.next= next
self.previous= previous
classQueue: def __init__(self): self.head=None self.tail=None self.count=0 def enqueue(self, data): new_node =TwoWayNode(data,None,None)if self.head is None: self.head= new_node
self.tail= self.headelse: new_node.previous= self.tail self.tail.next= new_node
self.tail= new_node
self.count+=1 def dequeue(self): current = self.headif self.count==1: self.count-=1 self.head=None self.tail=None elif self.count>1: self.head= self.head.next self.head.previous=None self.count-=1return current.data
classNode: def __init__(self, data): self.data= data
self.next=NoneclassQueue: def __init__(self): self.front=None self.rear=None def is_empty(self):return self.front is None def enqueue(self, item): new_node =Node(item)if self.rear is None: self.front= new_node
self.rear= new_node
else: self.rear.next= new_node
self.rear= new_node
def dequeue(self):if self.is_empty(): raise IndexError("Queue is empty") item = self.front.data self.front= self.front.nextif self.front is None: self.rear=Nonereturn item
def front(self):if self.is_empty(): raise IndexError("Queue is empty")return self.front.data def size(self): count =0 current = self.frontwhilecurrent: count +=1 current = current.nextreturn count
'''En esta implementación, utilizamos la clase Nodepara representar los nodos individuales en la cola.Luego, la clase Queuetiene un puntero al frente( front) y un puntero al final( rear) de la cola.Elmetodois_empty() verifica si la cola esta vacia.El método enqueue(item)agrega un elemento al final de la cola creando un nuevo nodo y actualizando el punterorear .El método dequeue()elimina y devuelve el elemento del frente de la cola, actualizando el puntero front.El metodo front()devuelve el elemento del frente de la cola sin eliminarlo.El método size()devuelve el tamaño real de la cola contando los nudos.'''
#Puedes utilizar la cola basada en nudos de la siguiente manera:# Crear una instancia de la cola
my_queue =Queue()# Verificar si la cola está vacía
print(my_queue.is_empty()) # Resultado:True# Agregar elementos a la cola
my_queue.enqueue(1)my_queue.enqueue(2)my_queue.enqueue(3)# Obtener el elemento del frente de la cola
print(my_queue.front()) # Resultado:1# Eliminar y obtener el elemento del frente de la cola
print(my_queue.dequeue()) # Resultado:1# Obtener el tamaño actual de la cola
print(my_queue.size()) # Resultado:2# Verificar si la cola está vacía nuevamente
print(my_queue.is_empty()) # Resultado:False'''La cola basada en nodos es útil cuando se requiere una estructura de datos dinámicos para administrar
elementos en orden de llegada.Se utiliza en situaciones donde se necesita procesar elementos de manera secuencial y respetando el principio
FIFO(FirstIn,FirstOut), como en colas de procesamiento de tareas, sistemas de planificación y
gestión de solicitudes.'''
el tail.next hace referencia al None, asi que es necesario modificar el codigo