Lista enlazada con Python - Estructura de datos y operaciones

Lista ligada en Python

En este post te mostraré una lista ligada en Python que después se podría modificar como una cola o pila (ya que se pueden insertar elementos al inicio y al final).

Las operaciones que manejaremos con esta lista enlazada son:

  • Agregar al inicio
  • Agregar al final
  • Saber si elemento existe
  • Eliminar un elemento
  • Obtener cabeza
  • Obtener cola
  • Recorrer lista

Por cierto, vamos a usar ciclos y no recursión para las operaciones. De esta manera el código queda más simple.

El nodo de la lista

La lista se compone de nodos o elementos que van enlazados entre sí. Cada nodo tiene dos cosas: el dato que guarda y un apuntador al nodo siguiente, del mismo tipo.

Python es un lenguaje bonito (no como C en donde tienes que solicitar memoria hasta para respirar) así que podemos guardar cualquier tipo de dato en los nodos, no importa si son cadenas, listas, objetos, números, etcétera.

class Nodo():
    dato = None
    siguiente = None

    def __init__(self, dato):
        self.dato = dato
        self.siguiente = None

Digamos que el dato será de tipo genérico.

Agregar elemento al final

Veamos cómo agregar un nodo al final de la lista enlazada usando Python. Lo que hacemos es recorrer la lista hasta el final y asignarlo ahí:

def agregar_al_final(nodo_inicial, dato):
    nuevo_nodo = Nodo(dato)
    if nodo_inicial == None:
        nodo_inicial = nuevo_nodo
        return nodo_inicial
    temporal = nodo_inicial
    while temporal.siguiente:
        temporal = temporal.siguiente
    temporal.siguiente = nuevo_nodo
    return nodo_inicial


Por cierto, si el nodo es None (eso sería cuando la lista está vacía) entonces el nuevo nodo será la cabeza de la lista (ya que sería el primero en la lista).

Añadir elemento al inicio de la lista enlazada

Ahora veamos la operación de agregar un elemento a la lista pero al inicio. En este caso solo debemos crear un nuevo nodo (que será la nueva cabeza o nuevo inicio) y hacer que su siguiente sea el que antes era el inicio:

def agregar_al_inicio(nodo_inicial, dato):
    nuevo_nodo = Nodo(dato)
    nuevo_nodo.siguiente = nodo_inicial
    return nuevo_nodo

Ten en cuenta que en ambas funciones que agregan un nodo a la lista (estructura de datos) de Python regresan un nodo que será el apuntador al inicio o cabeza de la lista.

Obtener inicio y final de cola/pila

Otra operación interesante con esta lista ligada de Python es la de obtener la cabeza o inicio, y el final o la cola. En este caso solo recorremos y regresamos el nodo.

def obtener_cabeza(nodo_inicial):
    return nodo_inicial


def obtener_cola(nodo_inicial):
    temporal = nodo_inicial
    while temporal.siguiente:
        temporal = temporal.siguiente
    return temporal

Obviamente para el caso de la cabeza simplemente regresamos el nodo que nos envían como argumento, ya que es el inicio de la lista.

Saber si elemento existe

Para saber si un elemento existe dentro de la lista enlazada que estamos creando basta recorrer toda la lista y comparar el dato de cada nodo con la búsqueda.

Si encontramos un dato de un nodo que coincida con la búsqueda entonces regresamos True.

def existe(nodo, busqueda):
    while nodo != None:
        if nodo.dato == busqueda:
            return True
        nodo = nodo.siguiente
    return False

En caso de terminar de recorrer la lista y no haber encontrado nada, regresamos False.

Nota: esto puede funcionar con objetos de cualquier tipo (incluso instancias de tus propias clases), solo debes implementar el método __eq__.

Eliminar elemento de lista enlazada con Python

Ahora veamos la operación en donde eliminamos un elemento. En este caso lo que hago es ir comparando el siguiente nodo del nodo actual, ya que no puedo ir hacia atrás en caso de que el elemento actual sea el que quiero eliminar.

Lo que hago cuando encuentro el elemento a eliminar es hacer que su elemento anterior apunte a su elemento siguiente.

def eliminar(nodo, busqueda):
    if nodo == None:
        return
    if nodo.dato == busqueda:
        return nodo.siguiente
    temporal = nodo
    while temporal.siguiente != None:
        if temporal.siguiente.dato == busqueda:
            if temporal.siguiente.siguiente != None:
                temporal.siguiente = temporal.siguiente.siguiente
            else:
                temporal.siguiente = None
                return nodo
        temporal = temporal.siguiente
    return nodo

Nota: igual y se podría hacer algo con el operador del de Python (así como lo hicimos en C++), pero supongo que el recolector de basura se encarga de liberar la memoria porque ya nadie tiene una referencia a ese nodo.

Poniendo todo junto

Lista enlazada con Python - Estructura de datos y operaciones
Lista enlazada con Python – Estructura de datos y operaciones

A continuación muestro un ejemplo en donde usamos las funciones expuestas y trabajamos con la lista enlazada.

Ten en cuenta que en este caso he usado cadenas pero puede usarse cualquier tipo de dato e incluso objetos.

class Nodo():
    dato = None
    siguiente = None

    def __init__(self, dato):
        self.dato = dato
        self.siguiente = None


def agregar_al_final(nodo_inicial, dato):
    nuevo_nodo = Nodo(dato)
    if nodo_inicial == None:
        nodo_inicial = nuevo_nodo
        return nodo_inicial
    temporal = nodo_inicial
    while temporal.siguiente:
        temporal = temporal.siguiente
    temporal.siguiente = nuevo_nodo
    return nodo_inicial


def agregar_al_inicio(nodo_inicial, dato):
    nuevo_nodo = Nodo(dato)
    nuevo_nodo.siguiente = nodo_inicial
    return nuevo_nodo


def imprimir_lista(nodo):
    while nodo != None:
        print(f"Tenemos {nodo.dato}")
        nodo = nodo.siguiente


def obtener_cabeza(nodo_inicial):
    return nodo_inicial


def obtener_cola(nodo_inicial):
    temporal = nodo_inicial
    while temporal.siguiente:
        temporal = temporal.siguiente
    return temporal


def existe(nodo, busqueda):
    while nodo != None:
        if nodo.dato == busqueda:
            return True
        nodo = nodo.siguiente
    return False


def eliminar(nodo, busqueda):
    if nodo == None:
        return
    if nodo.dato == busqueda:
        return nodo.siguiente
    temporal = nodo
    while temporal.siguiente != None:
        if temporal.siguiente.dato == busqueda:
            if temporal.siguiente.siguiente != None:
                temporal.siguiente = temporal.siguiente.siguiente
            else:
                temporal.siguiente = None
                return nodo
        temporal = temporal.siguiente
    return nodo


def main():
    lista = None
    lista = agregar_al_final(lista, "Luis")
    lista = agregar_al_final(lista, "Leon")
    lista = agregar_al_inicio(lista, "Link")
    print("Antes de eliminar: ")
    imprimir_lista(lista)  # Link, Luis, Leon
    lista = eliminar(lista, "Link")
    print("Después de eliminar: ")
    imprimir_lista(lista)  # Luis, Leon
    print(existe(lista, "Link"))  # False
    print(existe(lista, "Luis"))  # True
    # obtener_cabeza nos regresa el nodo, pero accedemos al dato para imprimirlo
    print(obtener_cabeza(lista).dato)  # Luis
    print(obtener_cola(lista).dato)  # Leon


main()

Por cierto, he dejado todas las funciones globales y sin pertenecer a una clase en común.

Esto es porque he hecho el ejemplo rápidamente y no pensé en hacerlo una clase desde un inicio. Incluso así, sería muy fácil agruparlo en una clase por ti mismo.

Para terminar te dejo con más tutoriales de Python y Estructura de datos en mi blog.

Bonus

Mientras grababa el vídeo explicativo hice un ejemplo con una clase para demostrar que se pueden usar objetos como datos de los nodos. Aquí el código:

class Nodo():
    dato = None
    siguiente = None

    def __init__(self, dato):
        self.dato = dato
        self.siguiente = None


def agregar_al_final(nodo_inicial, dato):
    nuevo_nodo = Nodo(dato)
    if nodo_inicial == None:
        nodo_inicial = nuevo_nodo
        return nodo_inicial
    temporal = nodo_inicial
    while temporal.siguiente:
        temporal = temporal.siguiente
    temporal.siguiente = nuevo_nodo
    return nodo_inicial


def agregar_al_inicio(nodo_inicial, dato):
    nuevo_nodo = Nodo(dato)
    nuevo_nodo.siguiente = nodo_inicial
    return nuevo_nodo


def imprimir_lista(nodo):
    while nodo != None:
        print(f"Tenemos {nodo.dato}")
        nodo = nodo.siguiente


def obtener_cabeza(nodo_inicial):
    return nodo_inicial


def obtener_cola(nodo_inicial):
    temporal = nodo_inicial
    while temporal.siguiente:
        temporal = temporal.siguiente
    return temporal


def existe(nodo, busqueda):
    while nodo != None:
        if nodo.dato == busqueda:
            return True
        nodo = nodo.siguiente
    return False


def eliminar(nodo, busqueda):
    if nodo == None:
        return
    if nodo.dato == busqueda:
        return nodo.siguiente
    temporal = nodo
    while temporal.siguiente != None:
        if temporal.siguiente.dato == busqueda:
            if temporal.siguiente.siguiente != None:
                temporal.siguiente = temporal.siguiente.siguiente
            else:
                temporal.siguiente = None
                return nodo
        temporal = temporal.siguiente
    return nodo


class Mascota():
    def __init__(self, nombre) -> None:
        self.nombre = nombre

    def __str__(self) -> str:
        return f"Mascota llamada {self.nombre}"

    def __eq__(self, __o: object) -> bool:
        return self.nombre == __o.nombre


def main():
    lista = None
    lista = agregar_al_final(lista, Mascota("Maggie"))
    lista = agregar_al_final(lista, Mascota("Grimm"))
    lista = agregar_al_inicio(lista, Mascota("Panqué"))
    lista = agregar_al_inicio(lista, Mascota("Ejemplo"))
    print("Antes de eliminar: ")
    imprimir_lista(lista)
    lista = eliminar(lista, Mascota("Ejemplo"))
    print("Después de eliminar: ")
    imprimir_lista(lista)  # Luis, Leon
    print(existe(lista, Mascota("Grimm")))  # False
    print(existe(lista, Mascota("Ejemplo")))  # False
    # obtener_cabeza nos regresa el nodo, pero accedemos al dato para imprimirlo
    print(obtener_cabeza(lista).dato)  # Luis
    print(obtener_cola(lista).dato)  # Leon


main()

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *