El diseño del nodo y las referencias de control determinan la flexibilidad y seguridad de cualquier lista enlazada. Entender cómo declararlos en Python, cómo exponerlos dentro de clases y cómo organizar el archivo es un paso obligado antes de implementar operaciones complejas.
Partimos de una clase que represente el valor almacenado y el enlace disponible. Adaptamos los campos según el tipo de lista; aquí mostramos la forma más común.
class Nodo:
def __init__(self, valor):
self.valor = valor
self.sig = None
El nodo guarda el dato y la referencia al siguiente. El tipo de valor puede ser cualquier objeto de Python. Inicializamos sig en None para indicar que el nodo aún no está enlazado.
La referencia cabeza es la entrada oficial a la lista. Se almacena fuera del nodo para mantener control sobre el conjunto completo.
# listas_enlazadas.py
class ListaSimple:
def __init__(self):
self.cabeza = None
def insertar_inicio(self, valor):
nuevo = Nodo(valor)
nuevo.sig = self.cabeza
self.cabeza = nuevo
La referencia a cabeza permite insertar y eliminar al inicio sin recorrer todo. Siempre debe inicializarse en None y actualizarse cada vez que la lista queda vacía.
Agregar una referencia cola (tail) acelera las inserciones al final. Se utiliza junto con cabeza cuando la estructura necesita operar en ambos extremos.
class ListaConCola:
def __init__(self):
self.cabeza = None
self.cola = None
def insertar_final(self, valor):
nuevo = Nodo(valor)
if self.cola is None:
self.cabeza = self.cola = nuevo
return
self.cola.sig = nuevo
self.cola = nuevo
La referencia a cola evita recorrer la lista para insertar al final. La condición especial es cuando la lista está vacía: ambas referencias deben apuntar al nuevo nodo.
La clase base se adapta según la variante elegida. A continuación se muestra una comparativa rápida:
| Tipo | Campos adicionales | Ventaja | Coste |
|---|---|---|---|
| Simple | sig |
Menor uso de memoria | No se puede retroceder |
| Doble | sig, ant |
Recorridos bidireccionales | Duplicación de referencias |
| Circular | sig conectando al inicio |
Recorridos continuos sin comprobaciones de None | Mayor atención para terminar ciclos |
En Python podemos mantener el nodo base y las operaciones en un solo archivo para facilitar la prueba rápida. Si el proyecto crece, basta con dividir en módulos, pero la estructura inicial puede ser así:
# listas_enlazadas.py
class Nodo:
def __init__(self, valor):
self.valor = valor
self.sig = None
class Lista:
def __init__(self):
self.cabeza = None
self.cola = None
def insertar_inicio(self, valor):
nuevo = Nodo(valor)
nuevo.sig = self.cabeza
self.cabeza = nuevo
if self.cola is None:
self.cola = nuevo
def insertar_final(self, valor):
nuevo = Nodo(valor)
if self.cola is None:
self.cabeza = self.cola = nuevo
return
self.cola.sig = nuevo
self.cola = nuevo
def limpiar(self):
while self.cabeza:
siguiente = self.cabeza.sig
self.cabeza.sig = None
self.cabeza = siguiente
def imprimir(self):
reco = self.cabeza
while reco:
print(reco.valor, end=" -> ")
reco = reco.sig
print("None")
def main():
lista = Lista()
lista.insertar_inicio(10)
lista.insertar_final(20)
lista.insertar_final(30)
lista.imprimir()
lista.limpiar()
if __name__ == "__main__":
main()
Flujo sugerido para probar: crear la lista (lista = Lista()), insertar 10 al inicio y luego 20 y 30 al final, y finalmente llamar a lista.imprimir() para ver 10 -> 20 -> 30 -> None. Al terminar, lista.limpiar() recorre la estructura, corta cada referencia sig y deja cabeza en None, de modo que el recolector pueda liberar los nodos sin referencias activas.
Con este esquema mantenemos en un solo lugar el nodo base, las referencias cabeza/cola y las operaciones esenciales. Más adelante se puede separar en módulos si aparece nueva funcionalidad.