No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Convierte tus certificados en títulos universitarios en USA

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

19 Días
12 Hrs
35 Min
51 Seg

Crear singly linked list

11/23
Recursos

Aportes 105

Preguntas 10

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Pienso que le falto explicar bien la “anatomía” de lo que creó, por donde empieza la lista, y en donde se ingresan los nuevos valores?

Dejo un video que me ayudó a entender el tema del “current” y self.tail
https://www.youtube.com/watch?v=oXuKUkIlv_o

Le falto mejor explicación, creo que solo se agarro hablando y escribiendo.

Que terrible explicación la del método delete… más que explicar, dictaste lo que estabas escribiendo pero no se entiende el motivo

:'v Donde esta facundo cuando uno lo necesita?

Lista enlazada lineal simple (singly linked list) – Implementación en Python

Una lista enlazada (linked list en inglés), es un tipo de estructura de datos compuesta de nodos. Cada nodo contiene los datos de ese nodo y enlaces a otros nodos.

Se pueden implementar distintos tipos de listas enlazadas. En este post vamos a implementar una lista enlazada lineal simple (singly linked list). En este tipo de listas, cada nodo contiene sus datos y un enlace al siguiente nodo. Además la lista tendrá un método para contar el número de elementos de la lista, un método para insertar un elemento en la lista y un método para eliminar un elemento de la lista.

En primer lugar, definimos una clase que va a ser la clase Node. Los objetos de esta contendrán sus propios datos y un enlace al siguiente elemento de la lista:

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

A continuación definimos la clase de la lista SinglyLinkedList, que contiene el primer elemento de la lista:

class SinglyLinkedList:
    def __init__(self, head):
        self.head = head

El método que cuenta los elementos de la lista, length(), primero comprueba que la lista no esté vacía, y luego recorre todos los elementos de la lista incrementando un contador por cada elemento. Al final devuelve el contador:

    def length(self) -> int: 
        current = self.head
        if current is not None:
            count = 1

            while current.next is not None:
                count += 1
                current = current.next
            return count
        else:
            return 0

El siguiente método, insert(datos, posición), inserta un elemento tras la posición indicada. Si se indica la posición 0, el nuevo elemento pasa a ser la cabecera de la lista. En esta implementación, si la posición que se pasa como argumento excede el tamaño de la lista,el elemento se inserta al final:

   def insert(self, data, position):
       new_node = Node(data)

       if position == 0:
           new_node.next = linked_list.head
           linked_list.head = new_node
       else:
           current = linked_list.head
           k = 1
           while current.next is not None and k < position:
               current = current.next
               k += 1
           new_node.next = current.next
           current.next = new_node

El método delete(posición) borra el elemento en la posición pasada como parámetro. Si es el primer elemento la lista de la cabeza pasa a ser el segundo elemento. Si se encuentra el elemento en la lista y se borra devolvemos True, en caso contrario devolvemos False:

    def delete(self, position):
       if position != 1:
           current = self.head
           k = 1
           while current.next is not None and k < position - 1:
               current = current.next
               k += 1
           if current.next is not None:
               current.next = current.next.next
               return True
           else:
               return False
       else:
           self.head = self.head.next
           return True

Creamos la lista

linked_list = SinglyLinkedList(Node(1))

Rellenamos la lista

 for i in range(2,10):
        linked_list.insert(i, i-1)

Insertamos un elemento

    linked_list.insert(999,3)

Eliminamos un elemento

    linked_list.delete(6)

Mostramos la lista

    current = linked_list.head
    while current is not None:
	print(current.data)
        current = current.next

Sinceramente entendí poco y nada. Todas las clases desde Array se las ha saltado sin explicaciones de como funciona cada cosa dejándonos a la deriva. Espero que no sea así en el futuro.

Solución

 # Reto
    array = [2, 4, 6]
    datos = SinglyLinkedList()

    for i in array:
        datos.append(i)
    current = datos.tail
    
    while current:
        print(current.data)
        current = current.next

Acá mismo abandono el curso, la explicación del profesor no se alinea para NADA con la ruta de aprendizaje que sugieren en “Desarrollo Backend con Python”. Una lástima

Metodo delete gráficamente:

Creo que pasa mucho en varias clases de los cursos que los profesores o saben exactamente como explicar un tema que solo van tirando código como locos si explicar de forma entendible que va haciendo cada línea, y creo que este es el caso de esta clase

from node import Node
class LinkedList:
	def __init__(self):
		self.head=None

	def insert_at_begining(self, data):
		node=Node(data,self.head)
		self.head=node

	def insert_at_end(self, data):
		#caso de que sea el primero
		if self.head is None:
			self.head=Node(data, None)
			return
		itr =self.head
		while itr.next:
			itr=itr.next

		itr.next=Node(data, None)

	#de una Lista a una LinkedList
	def insert_value_from_list(self, data_list):
		self.head=None
		for data in data_list:
			self.insert_at_end(data)
def print(self):
		if self.head is None:
			print("Linked list is empty")
			return

		itr=self.head
		llstr=''
		
		while itr:
			llstr+=str(itr.data) + '-->'
			itr =itr.next

		print(llstr)
	
if __name__=="__main__":
	ll=LinkedList()
	ll.insert_value_from_list(["banana","mango","fresa"])
	ll.print()

Hola, creo que la función “Delete” del profesor está mala. Me corrigen si estoy equivocado pero creo que la forma correcta es la siguiente:

def delete(self, data):

    current = self.tail
    prev =current

    while current:

        if current.data == data:

            if current == self.tail:

                self.tail = current.next
            
            else:

                prev.next = current.next
                current = current.next
                self.size -= 1
            
            return current.data

        else:

            prev = current
            current = current.next

Pues logré hacer que mostrará el mensaje cuando no encuentra un dato. Además logré implementar head & tail en esta lista:

from node import Node

class SinglyLinkedList:
  def __init__(self):
    self.head = None
    self.tail = None
    self.size = 0


  def append(self, data):
    node = Node(data)

    if self.tail == None and self.head == None:
      self.head = node
      self.tail = node
    else:
      current = self.tail

      while current.next:
        current = current.next

      current.next = node
      self.tail = current.next

    self.size += 1


  def size(self):
    return str(self.size)


  def iter(self):
    current = self.tail

    while current:
      value = current.data
      current = current.next
      yield value #* Genera valores pero NO los almacena


  def delete(self, data):
    current = self.tail
    previous = self.tail

    while current:
      if current.data == data:
        if current == self.tail:
          self.tail = current.next
        else:
          previous.next = current.next
          self.size -= 1
          return current.data

      previous = current
      current = current.next


  def search(self, data):
    flag = False
    for node in self.iter():
      if data == node:
        flag = True
        print(f'Data {data} found 😎')
    if not flag:
      print(f'Data {data} not found 😞')


  def clear(self):
    self.tail = None
    self.head = None
    self.size = 0


games = SinglyLinkedList()
games.append('GTA V')
print(f'head: {games.head.data} & tail: {games.tail.data}')
games.append('Fifa')
print(f'head: {games.head.data} & tail: {games.tail.data}')
games.append('Mario')
print(f'head: {games.head.data} & tail: {games.tail.data}')

NO deberíamos depender de los aportes de los compañeros para poder entender todas las clases, realmente es un desastre la pedagogía del profesor Héctor Vega.

pesima clase, no explica nada , primero debe explicar de modo grafico que se quiere hacer , empieza a hacer código sin explicar nada

Les comparto una imagen que lo explica de forma grafica como es la secuencia en la lista
![](

Sí alguien le quedo la duda de como ordena la linked list, pues esta usando el algoritmo de ordenamiento de Burbuja, que en big O notation es O(n°2)

Hice un método importando la clase array de las clases anteriores

Y le hice un cambio al metodo search

Otra manera de verlo

class SingleLinkedList:
    def __init__(self) -> None:
        self.head = Node("head")
        self.length = 0

    def __iter__(self):
        current_node = self.head

        while current_node.next:
            current_node = current_node.next
            yield current_node.data
    
    def append(self, data):
        current_node = self.head
        
        while current_node.next:
            current_node = current_node.next

        current_node.next = Node(data)
        self.length += 1
    
    def delete(self, data):
        current_node = self.head

        while current_node.next:
            prev_node = current_node
            current_node = current_node.next
            if current_node.data == data:
                prev_node.next = current_node.next
                del current_node
                current_node = prev_node
                self.length -= 1
    
    def clear(self):
        for item in self:
            self.delete(item)

    def search(self, data):
        for node in self:
            if node == data:
                print(f"found {node}")

    def length(self):
        return str(self.length)

El zen de python 🐱‍👤

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

llamen al metodo size de otra forma, ya que existe un atributo llamado size.

array = ['hola', 'como','estas', '?']
words = SingleLinkedList()

for i in array:
	words.append(i)
for item in words.iter():
	print(item)

Espero que platzi agregue una clase de generadores, creo que hasta ahora en los cursos que he tomado de python no lo han explicado.
Les recomiendo aprender sobre ellos ya que son muy importantes cuando se van a iterar los datos pero no queremos guardarlos en memoria.

En el método iter de nuestra clase se usa el keyword yield que permite crear un generador.

Si les gusta leer más sobre ellos:
https://wiki.python.org/moin/Generators
https://es.stackoverflow.com/questions/6048/cuál-es-el-funcionamiento-de-yield-en-python/6084


En momentos como este es cuando detesto mi dislexia, me confundí con el reto no se si era hacer que los nodos se comportaran como un array de dos dimensiones o convertir el array de tipo matriz de las clases pasadas a nodos usando un método de conversión, o reconvertir la clase nodo para que fuera una matriz T^T
Bueno aquí mi código

class Nodo:
    def __init__(self, valor, siguiente_nodo = None):
        self.valor = valor
        self.siguiente_nodo = siguiente_nodo


class Lista_nodo:

    def __init__(self):
        self.limpiar()



    def limpiar(self):
        self.nodo_actual = None
        self.__SEGURO__ = False
        self.nodo_tmp = None
        self.tam = 0



    def append(self,valor):
        nodo_nuevo = Nodo(valor)
        self.tam += 1

        if not self.nodo_actual:
            self.nodo_actual = nodo_nuevo
            return

        nodo_siguiente = self.nodo_actual

        while nodo_siguiente.siguiente_nodo:
            nodo_siguiente = nodo_siguiente.siguiente_nodo

        nodo_siguiente.siguiente_nodo = nodo_nuevo



    def index(self,indice):
        if not self.tam:
            raise AssertionError("Error sin elementos")

        def INDEX(INDICE):
            if INDICE > self.tam - 1 or INDICE < 0:
                raise AssertionError("Error de indice fuera de los limites")
            i = 0
            nodo = self.nodo_actual
            while i != INDICE:
                self.nodo_tmp = nodo
                nodo = nodo.siguiente_nodo
                i += 1
            if self.__SEGURO__:
                return nodo.siguiente_nodo
            return self.nodo_tmp.valor

        if indice < 0:
            return INDEX(self.tam + indice)
        else:
            return INDEX(indice)
            
                

    def pop(self,indice = -1):
        self.__SEGURO__ = True
        try:
            self.nodo_tmp.siguiente_nodo = self.index(indice)
            self.tam -= 1
        except AssertionError:
            raise AssertionError("Indice inexistente")
        finally:
            self.__SEGURO__ = False
            


    def find(self,valor):
        for val , indice in self.iter_items():
            if val == valor:
                print("valor encontrado")
                return indice
        print("el valor no existe")
        return None



    def printing(self,elementos_mostrados_en_fila = 10):
        print("|",end="")
        salto = elementos_mostrados_en_fila - 1
        for val, i in self.iter_items():
            if i < salto:
                print(f" {val} |",end="")
            else:
                salto += elementos_mostrados_en_fila - 1
                print("")
        print("")



    def iter(self):
        for val, i in self.iter_items():
            yield val



    def iter_items(self):
        nodo_siguiente = self.nodo_actual
        if not nodo_siguiente:
            return
        i = 0
        while nodo_siguiente.siguiente_nodo:
            val = nodo_siguiente.valor
            nodo_siguiente = nodo_siguiente.siguiente_nodo
            yield (val, i)
            i += 1
        yield (nodo_siguiente.valor,i)



    def absorver(self, estructura_de_datos):
        # Eto... la neta no entendi el reto XD
        pass

if __name__ == "__main__":
    from random import randint
    lista = Lista_nodo()

    print("\nmetodo append [ Nodo.append(",end="")
    for i in range(1,8):
        val = randint(1,20)
        print(f"{val},",end="")
        lista.append(val)
    print(") ]\n")
    print("metodo pop [Nodo.pop(\"con o si indice\")]\n")
    lista.printing()
    i = -2
    lista.pop(i)
    print(f"Nodo.pop({i})")
    lista.printing()
    print("\nmetodo index [Nodo.index(int)]\n")
    print(f"Nodo[{i}] = {lista.index(i)}")
    print("\nmetodo iter [Nodo.iter()]\n")
    for val in lista.iter():
        print(val,end= ",")
    print("\n\nmetodo iter_items [Nodo.iter_items()]\n")
    for val, idx in lista.iter_items():
        print(f"[{idx}] = {val}",end=" | ")
    print("\n\nmetodo find [Nodo.find(\"valor\")]\n")
    j = 11
    print(f"indice = {lista.find(j)} Nodo.find({j})")

Código comentado con detalles con reto de Search y usando variables head and tail

para una mejor comprensión, Bing me ayudo lo admito, porque el profe pff naa solo transcribió y mal porque trunca tail por head confundiendo mas.

Reto Array con LL:

use el append method del código, pues al usar el de head entraría en otro orden los nodos, y eso ayuda a entender mejor el porque de las cosas.

me pesa el cerebro jajajajajaja

Les dejo la solucion al reto, hice algunas modificaciones a la rutina para crear un array

class array:
    def __init__(self,capacity,fill_value = None):
        self.capacity = capacity
        self.items =list()
        for i in range(capacity):
            self.items.append(fill_value)

    def __len__(self):
        return len(self.items)
    

    def __str__(self) -> str:
        return str(self.items)

    
    def __iter__(self):
        return iter(self.items)

    def __getitem__(self,index):
        return str(self.items[index])

    def __setitem__(self,index,new_item):
        self.items[index] = new_item

    def __randomnumbers__(self):
        for i in range(self.capacity):
            self.__setitem__(i,random.randint(0,9))
        return self.items

    def __sumelements__(self):
        count = 0 
        for i in range(self.capacity):
            count += self.items[i]
        return count
    #Metodo para insertar elementos por medio del teclado al array
    def __insert_elements__(self):
        for i in range(self.capacity):
            self.__setitem__(i,input("Ingresa un dato: "))










from crear_node import Node
from crear_array import array

class linked_list:
    
    def __init__(self) -> None:
        self.tail = None #es none por que es el ultimo nodo 
        self.size = 0 #0 por que no hay nodos
    
    def __append__(self,data):
        node = Node(data) #Entra un dato y con mi clase nodo creo un nodo ocn un valor y un puntoro como none
        if self.tail == None:
            self.tail = node # si no hay nodos en mi lista voy a decir que mi colo es igual al primer nodo por que no habia nodos 
        else:
            current = self.tail #pero si hay nodo voy decir que mi estado actual es donde esta mi cola

            while current.next: #mientras que haya algo que iterar avanzo 
                current = current.next  # como hay algo avanzo a la siguiente posicion de memoria

            current.next = node #y eso que avanze ahi coloco mi nuevo nodo bien bonito y aumento en 1 el tamaño de mi lista

        self.size += 1


    def __size__(self):
        return str(self.size)

    def __iter__(self):
        current = self.tail #me voy al final de mi lista
        while current: #mientra que haya algo en current
            val = current.data #mi valor actual donde estoy
            current = current.next #avanzo  a mi siguiente valor
            yield val #regeso el valor donde estoy 

    def __delete__(self,data):
        current = self.tail
        previous = self.tail

        while current:
            if current.data == data:
                if current == self.tail:
                    self.tail == current.next
                else:
                    previous.next = current.next
                    self.size -= 1
                    return current.data
            
            previous = current
            current = current.next
        

    def __search__(self,data):

        for node in self.__iter__():
            if data == node:
                print(f"Data {data} found")
    
    def __clear__(self):
        self.tail = None
        self.head = None
        self.size = 0

if __name__ == '__main__':
    # words = linked_list()
    # words.__append__("Salmon")
    # words.__append__("Atun")
    # words.__append__("Queso")
    # current = words.tail
    # while current:
    #     print(current.data)
    #     current = current.next
    # print(words.__size__())
    # words.__delete__("Queso")
    # current = words.tail
    # while current:
    #     print(current.data)
    #     current = current.next
    # print(words.__size__())
    # words.__search__("Salmon")
    words = array(5)
    words.__insert_elements__()
    print(words.__str__())
    list_of_words  = linked_list()
    for i in range(words.capacity):
        #print(words.__getitem__(i))
        list_of_words.__append__(words.__getitem__(i))
    current = list_of_words.tail
    while current:
        print(current.data)
        current = current.next






comparto mi version del reto, si hay algun error porfavor me avisan

from array1 import Array
from linked_list import SinglyLinkedList

# RETO

if __name__ == "__main__":
    
    num_array = Array(5)

    for i in range(5):
        num_array.__setitem__(i,i)
    
    num_linked_list = SinglyLinkedList()

    for num in num_array.__iter__():
        num_linked_list.append(num)
    
    current = num_linked_list.tail

    while current:
        print(current.data)
        current = current.next
Esto fue lo que hice (fue una muy pequeña modificación):from node import Node class SinglyLinkedList:    def \_\_init\_\_(*self*):        *self*.tail = None        *self*.size = 0     def append(*self*, *data*):         for i in *data*:            node = Node(i)             if *self*.tail == None:                *self*.tail = node            else:                current = *self*.tail                 while current.next:                    current = current.next                 current.next = node             *self*.size += 1 ```python from node import Node class SinglyLinkedList: def __init__(self): self.tail = None self.size = 0 def append(self, data): for i in data: node = Node(i) if self.tail == None: self.tail = node else: current = self.tail while current.next: current = current.next current.next = node self.size += 1 if __name__ == '__main__': words = SinglyLinkedList() words.append(['egg', 'ham', 'jam']) for word in words.iter(): print(word) # egg # ham # jam ```
````txt ## Ejemplos de referencias cruzadas y complejas * **Nodos Simples con referencias a otros nodos** ```py class Node: def __init__(self, data): self.data = data self.next = None # Creando nodos node1 = Node(10) node2 = Node(20) node3 = Node(30) # Enlazando nodos node1.next = node2 node2.next = node3 # En este ejemplo basico, cada nodo tien un atributo next que apunta al siuiente nodo en la lista ``` * **Nodos con Múltiples Referencias** ```py class Node: def __init__(self, data): self.data = data self.children = [] # Lista para almacenar referencias a hijos # Creando un árbol root = Node('root') child1 = Node('child1') child2 = Node('child2') root.children.append(child1) root.children.append(child2) # Aqui, cada nodo puede tener multiples hijos, creando una estructura de arbol ``` * **Nodos con Referencias Circulares** ```py class Node: def __init__(self, data): self.data = data self.next = None # Creando un ciclo node1 = Node(10) node2 = Node(20) node3 = Node(30) node1.next = node2 node2.next = node3 node3.next = node1 # Creando el ciclo # Las referencias circulares osn utiles para representar estructuras ciclicas como grafos dirigidos. ``` ### Referencias Cruzadas Complejas y Casos de Uso * **1. Grafos:** Los grafos son estructuras de datos que consisten en nodos (vértices) y aristas (conexiones). Cada nodo puede tener múltiples aristas que lo conectan a otros nodos, creando una red compleja de referencias cruzadas. * **2. Árboles:** Los árboles son estructuras de datos jerárquicas donde cada nodo tiene un único padre (excepto la raíz) y puede tener múltiples hijos. * **3. Listas enlazadas:** Las listas enlazadas son estructuras de datos lineales donde cada elemento (nodo) contiene un enlace al siguiente elemento. * **4. Redes sociales:** Las redes sociales son un ejemplo de grafos donde los usuarios son nodos y las conexiones entre ellos representan amistades, seguidores, etc. #### Caso de Uso: Implementacion de un Arbol Genealogico ```py class Person: def __init__(self, name, parents=None): self.name = name self.parents = parents or [] self.children = [] # Creando personas person1 = Person('Juan') person2 = Person('Maria') child1 = Person('Ana', [person2, person1]) # Agregando hijos person1.children.append(child1) person2.children.append(child1) # Imprimos la lista print(person2) print(person1) print(child1) # En este ejemplo, cada persona puede tener multiples padres y multiples hijos, creando un arbol genealogico ``` ## Consideraciones importantes * **Manejo de ciclos:** Las referencias circulares pueden causar problemas si no se manejan correctamente, como bucles infinitos. * **Eficiencia:** La elección de la estructura de datos adecuada depende de las operaciones que se realizarán con mayor frecuencia. * **Complejidad:** Las estructuras de datos con muchas referencias cruzadas pueden ser más difíciles de implementar y depurar. ````
```js def search(self, data): #método de busqueda for node in self.iter(): if data == node: print(f"data {data} found!!") else: print("non-existent data") ``` def search(self, data): #método de busqueda for node in self.iter(): if data == node: print(f"data {data} found!!") else: print("non-existent data")
```js import random #Importamos random para su uso posterior. class Array: #Creamos la clase Array, tendra métodos y atributos para un array. def __init__(self, capacidad, item = None): #El constructor, pedimos la capacidad del array. self.items = list() #Cremamos el array como una lista en python. for i in range(capacidad): #Usamos este for para iterar hasta el valor dado en el objeto. self.items.append(item) #Agregamos item a los valores del array self.items def __getitem__(self, index): #Método __getitem__ -> Retornamos el valor que nos pida el usuario con el index del array. return self.items[index] #Retorno. def __setitem__(self, index, value): #Método __setitem__ -> Cambiamos el valor de uno de los array al que nos pida el usuario. self.items[index] = value #Cambio return self.items #Retorno con el cambio def __len__(self):#Método __len__ -> Retornamos la longitud del array. return len(self.items) def __str__(self):#Método __str__ -> Retornamos el array como un str. return str(self.items) def __iter__ (self): # Método __iter__ -> Retornamos un iterador del array. return iter(self.items) def __replace_item__(self): #Método __replace_item__ -> Replazamos los valores del array. value = input("¿Como prefieres remplazar tus valores? Aleatorios -> A o Secuenciales -> S: ") #Le damos 2 opciones al usuario a cambiar los valores y los guardamos. value = value.upper() #Este valor lo pasamos a Mayuscula para evitar problemas. while value != "A" and value != "S": #Iterador en caso de que el usuario ponga un valor erroneo. value = input("\nLos valores que me diste no son correctos; A para Aleatorios o S para Secuenciales: ")#Pedimos el valor de nuevo. value = value.upper() #Lo volvemos a pasar a mayuscula. if value == "A": #Si el valor es A... for i in range(self.__len__()): #Ietador para cambia pasar por cada valor del array. self.items[i] = random.randint(1,100) #Usando la función randint de la biblioteca randomo con un intervalo de 1 a 100, guardamos números al azar. return self.items #Retornamos el array. elif value == "S": #Si el valor es S valor = 1 #Lo inicializamos con 1 for i in range(self.__len__()): #Iteramos cada valor del array. self.items[i] = valor #Guardamos valor en el array valor += 1 #Sumamos 1 al valor para la siguiente iteración return self.items #Retornamos el array terminado la iteración. def __llenar__(self): for i in range(self.__len__()): self.items[i] = i + 1 def __sum_items__(self): #Método __sum_items__ -> Suma los valores del array. iterator = self.__iter__() #Creamos un iterador con el método iter. primer_v = next(iterator) #Guardamos el primer valor del array if isinstance(primer_v,int): #Si el primer valor es entero for elemento in iterator: #Itermamos el array con el iterador. primer_v += elemento #Sumamos al primer_v return primer_v #Retoransmo la suma. elif isinstance(primer_v,str): #Si el primer valor es un caracter o string. for elemento in iterator: #Iteramos el array con el iterador. primer_v += elemento #Vamos sumando al iterado. return primer_v #Retornamos la cadena. else: #En caso de que el array tenga distintos tipos de valores raise ValueError ("Todos los elementos deben ser del mismo tipo para ser sumados. ") #Lanzamos un ValueError... if __name__ == "__main__": array = Array(5) print(array.__len__()) print(array.__str__()) print(array.__getitem__(4)) array.__llenar__() for value in array.__iter__(): print(f"El valor es: {value}") ``````python class Nodo():#Creamos la clase Nodo def __init__(self, value, next = None): self.value = value #Atributo 1: el valor del nodo self.next = next #Atributo 2: El puntero para el siguiente nodo. if __name__ == "__main__": #Nos permite usar la clase como script o como módulo. Node = Nodo(234) #Primer nodo, no apunta a ningun lado(cola). Node_3 = Nodo(44, Node) #Segundo nodo, apunta al valor del primer nodo y tiene su propio valor. Node_2 = Nodo(32, Node_3) #Tercer Nodo, apunta al segundo nodo y tiene su propio valor. Node_4 = Nodo(52, Node_2) #Cuarto Nodo, apunta al tercer nodo y tiene su propio valor. Node_5 = Nodo(42, Node_4) #Quinto nodo, apunta al cuarto nodo y tiene su propio valor. head = " " #Creamos la variable head con nada. for i in range(5): #En un rango de 0 a 4, itera... head = Nodo(i,head) #head tenga el valor del iteradór y apunte a head #Esto crear un conjunto de nodos conectados en donde el ultimo es el 4 y el primero es 0, 0 no tiene puntero. while head != None: #Mientras el puntero de head no sea none print(head.value) #imprime el valor de head head = head.next #pasa al siguiente nodo #Se van imprimiendo de forma inversa, 4,3,2.. porque el ultimo en iterar es el primer nodo. ``````python from Nodo_u import Nodo from array_n import Array class SinglyLinkedList: def __init__(self): self.head = None self.tail = None self.long = 0 def append(self, value): nodo = Nodo(value) if self.long == 0: self.head = nodo self.tail = nodo else: value = self.tail self.tail = nodo value.next = self.tail self.long += 1 def len(self): return self.long def getitem(self): current = self.head if current == None or self.long == 0: raise Exception("No tienes nada en tu lista. ") else: for i in range(self.long): print(current.value) current = current.next print("----------------- ") print(self.head.value) print(self.tail.value) def iter(self): current = self.head for i in range(self.len()): yield current.value current = current.next def delete(self, value): current = self.head if self.head == None: raise Exception("No hay valores en tu lista. ") elif self.head.value == value: self.head = current.next else: while current.next.value != self.tail.value and current.next.value != value: current = current.next if current.next.value == self.tail.value and value == self.tail.value: self.tail = current elif current.next.value == value: current.next = current.next.next self.long -= 1 def search(self, value): current = self.head while current != None: if current.value == value: return (f"{value} si esta en la lista.") current = current.next return (f"{value} no esta en la lista.") def clear(self): self.head = None self.tail = None self.long = 0 def implemetacion(self): array = Array(5) array.__llenar__() for a in array.__iter__(): list.append(a) list = SinglyLinkedList() list.append(12) list.append(13) list.append(14) list.append(15) list.append(16) list.append(17) list.append(18) list.append(19) print("\nLa lista contiene los elementos: \n") list.getitem() print(f"\nSu longitud es de: {list.len()} elementos \n") print(f"Retornamos el valor si es que lo tiene, en este caso: {list.search(13)}") print("\nEliminamos un valor y devolvemos la lista y la longitud ") print("Eliminamos el número 13 \n") list.delete(12) print("La lista contiene los elementos: \n") for valor in list.iter(): print(f"El valor es: {valor}") print(f"\nLa nueva longitud es de: {list.len()} elementos \n") list.clear() list.implemetacion() for i in list.iter(): print(f"El valor de la lista es:{i}") print(f"\nLa nueva longitud es de: {list.len()} elementos \n") ```
Implementacion de cuando no consigue un dato en la lista: ![](https://static.platzi.com/media/user_upload/image-75d657b6-1b77-4167-8c66-9e6b28f5dbb9.jpg)
para los que no entendieron el funcionamiento de la palabra reservada ***yield*** <https://alvarohurtado.es/2020/06/08/que-hace-yield-en-python/>
Reto completado 🏆🥳 ![](https://static.platzi.com/media/user_upload/image-5549b774-d3bf-4c95-817a-d0eda010b81f.jpg)

reto:

num = [x for x in range(1, 10)]
for i in num:
    my_list.append(i)

implemente mi propio metodo delete que elimina el nodo sin importar el lugar en que se encuentre

Les dejo el código comentado para mejor entendimiento: ```js from node import Node # Importa la clase Node desde el archivo node.py class SinglyLinkedList: def __init__(self): self.head = None # Inicializa la cabeza de la lista como None self.size = 0 # Inicializa el tamaño de la lista como 0 def add_to_start(self, data): """Inserta un nodo al inicio de la lista.""" self.head = Node(data, self.head) # Crea un nuevo nodo con el dato y lo enlaza al nodo actual en la cabeza print(f"Nodo {self.head.data} añadido al inicio.") self.size += 1 def append(self, data): """Añade un nodo nuevo al final de la lista.""" node = Node(data) # Crea un nuevo nodo con el dato if self.head == None: # Si la lista está vacía self.head = node # El nuevo nodo se convierte en la cabeza else: probe = self.head while probe.next: probe = probe.next probe.next = node # Enlaza el nuevo nodo al último nodo existente print(f"Nodo {node.data} añadido al final.") self.size += 1 def insert(self, data, index): """Inserta un nodo en una posición dada.""" if index > self.count_nodes(): print("El índice supera la cantidad de nodos.") else: if self.head is None or index <= 0: # Si la lista está vacía o se inserta al inicio self.head = Node(data, self.head) # Inserta el nuevo nodo como cabeza else: probe = self.head index -= 1 while index > 1 and probe.next != None: probe = probe.next index -= 1 probe.next = Node(data, probe.next) # Inserta el nuevo nodo después del nodo en la posición index print(f"Nodo {data} insertado.") self.size += 1 def replace(self, data, new_data): """Reemplaza el valor de un nodo dado.""" probe = self.head while probe != None and data != probe.data: probe = probe.next if probe != None: probe.data = new_data print(f"El nodo {data} ha sido reemplazado por {new_data}.") else: print(f"El nodo {data} no se encuentra en la lista.") def delete(self, index): """Elimina un nodo en una posición dada.""" if index > self.count_nodes(): print("El índice supera la cantidad de nodos.") else: if self.head is None: print("No hay nodos que eliminar.") elif self.head.next is None: print(f"Nodo: {self.head.data} eliminado.") self.head = None self.size -= 1 else: probe = self.head index -= 1 while index > 1 and probe.next.next != None: probe = probe.next index -= 1 removed_item = probe.next.data probe.next = probe.next.next print(f"Nodo:{removed_item} eliminado.") self.size -= 1 def pop(self): """Elimina el último nodo de la lista.""" data = self.head.data if self.head.next is None: self.head = None else: probe = self.head while probe.next.next != None: probe = probe.next data = probe.next.data probe.next = None self.size -= 1 print(f"Último nodo {data} eliminado.") def search(self, data): """Busca un nodo con un valor específico.""" probe = self.head while probe != None and data != probe.data: probe = probe.next if probe != None: print(f"El nodo {data} ha sido encontrado.") return probe else: print(f"El nodo {data} no se encuentra en la lista.") def count_nodes(self): """Devuelve la cantidad de nodos en la lista.""" return self.size def print(self): """Imprime todos los nodos de la lista.""" probe = self.head while probe != None: print(probe.data) probe = probe.next def clear(self): """Elimina todos los nodos de la lista.""" self.head = None self.size = 0 ```from node import Node  # Importa la clase Node desde el archivo node.py class SinglyLinkedList:    def \_\_init\_\_(*self*):        *self*.head = None  # Inicializa la cabeza de la lista como None        *self*.size = 0      # Inicializa el tamaño de la lista como 0     def add\_to\_start(*self*, *data*):        """Inserta un nodo al inicio de la lista."""        *self*.head = Node(*data*, *self*.head)  # Crea un nuevo nodo con el dato y lo enlaza al nodo actual en la cabeza        print(f"Nodo {*self*.head.data} añadido al inicio.")        *self*.size += 1     def append(*self*, *data*):        """Añade un nodo nuevo al final de la lista."""        node = Node(*data*)  # Crea un nuevo nodo con el dato        if *self*.head == None:  # Si la lista está vacía            *self*.head = node   # El nuevo nodo se convierte en la cabeza        else:            probe = *self*.head            while probe.next:                probe = probe.next            probe.next = node  # Enlaza el nuevo nodo al último nodo existente        print(f"Nodo {node.data} añadido al final.")        *self*.size += 1     def insert(*self*, *data*, *index*):        """Inserta un nodo en una posición dada."""        if *index* > *self*.count\_nodes():            print("El índice supera la cantidad de nodos.")        else:            if *self*.head is None or *index* <= 0:  # Si la lista está vacía o se inserta al inicio                *self*.head = Node(*data*, *self*.head)  # Inserta el nuevo nodo como cabeza            else:                probe = *self*.head                *index* -= 1                while *index* > 1 and probe.next != None:                    probe = probe.next                    *index* -= 1                probe.next = Node(*data*, probe.next)  # Inserta el nuevo nodo después del nodo en la posición index            print(f"Nodo {*data*} insertado.")            *self*.size += 1     def replace(*self*, *data*, *new\_data*):        """Reemplaza el valor de un nodo dado."""        probe = *self*.head        while probe != None and *data* != probe.data:            probe = probe.next        if probe != None:            probe.data = *new\_data*            print(f"El nodo {*data*} ha sido reemplazado por {*new\_data*}.")        else:            print(f"El nodo {*data*} no se encuentra en la lista.")     def delete(*self*, *index*):        """Elimina un nodo en una posición dada."""        if *index* > *self*.count\_nodes():            print("El índice supera la cantidad de nodos.")        else:            if *self*.head is None:                print("No hay nodos que eliminar.")            elif *self*.head.next is None:                print(f"Nodo: {*self*.head.data} eliminado.")                *self*.head = None                *self*.size -= 1            else:                probe = *self*.head                *index* -= 1                while *index* > 1 and probe.next.next != None:                    probe = probe.next                    *index* -= 1                removed\_item = probe.next.data                probe.next = probe.next.next                print(f"Nodo:{removed\_item} eliminado.")                *self*.size -= 1     def pop(*self*):        """Elimina el último nodo de la lista."""        data = *self*.head.data        if *self*.head.next is None:            *self*.head = None        else:            probe = *self*.head            while probe.next.next != None:                probe = probe.next            data = probe.next.data            probe.next = None        *self*.size -= 1        print(f"Último nodo {data} eliminado.")     def search(*self*, *data*):        """Busca un nodo con un valor específico."""        probe = *self*.head        while probe != None and *data* != probe.data:            probe = probe.next        if probe != None:            print(f"El nodo {*data*} ha sido encontrado.")            return probe        else:            print(f"El nodo {*data*} no se encuentra en la lista.")     def count\_nodes(*self*):        """Devuelve la cantidad de nodos en la lista."""        return *self*.size     def print(*self*):        """Imprime todos los nodos de la lista."""        probe = *self*.head        while probe != None:            print(probe.data)            probe = probe.next     def clear(*self*):        """Elimina todos los nodos de la lista."""        *self*.head = None        *self*.size = 0
Faltó mucha más pedagogía en esta clase
Les comparto mi código para los métodos **append** y **delete**, espero les sirva y entiendan mi explicación 😀 ```python from nodos_aux import Node class SinglyLinkedList: def __init__(self): # Defino 3 atributos iniciales: head, tail y size. self.head = None self.tail = None self.size = 0 def append(self, data): # Nodo que se va a agregar node = Node(data) # Si la lista esta vacía (lo que es equivalente a que size = 0), head y tail # apuntarán al elemento que se va a agregar (node) porque sería el único elemento de la lista. if self.size == 0: self.tail = node self.head = node self.size += 1 # Si la lista no esta vacía else: current = self.tail # Asignamos el último nodo (tail) a la variable auxiliar current para usarla en la siguiente linea. current.next = node # Agregamos al útimo nodo (al que ahora apunta current) la referencia (next) hacia el nodo que se esta agregando (node). Esto quiere decir que ahora el nodo que era último ya no lo es porque se le agrego la referencia hacia el nuevo nodo agregado. self.tail = node # Asignamos el nodo que se ha agregado a tail, ya que ahora este será el ultimo nodo. Comentar que head queda sin modificarse ya que al agregar nuevos nodos head no cambia. self.size += 1 def delete(self, data): # Variables auxiliares current = self.head # current apunta al primer nodo (head) previous = self.head # previous apunta al primer nodo (head) data_deleted = None # Si la lista esta vacía (lo que equivale a que size = 0) if self.size == 0: print('You can not delete anything, the list is empty!') return None # Al declarar "return None" ya no se ejecutarán as líneas de código siguientes # La lista no esta vacía, entonces se continua ejecutando las siguientes líneas # Si la data a eliminar coincide con la data de head (nodo al que apunta current), se "elimina" este nodo y ... if current.data == data: self.head = current.next # Ahora head apuntará al siguiente nodo o a None (si solo hay un nodo en la lista) if self.size == 1: # Además, si solo hay un nodo en la lista y se elimina ese nodo ... self.tail = None # Entonces tail apuntará a None, comentar que si hay mas de un nodo y se elimina head, tail no cambiaría self.size -= 1 data_deleted = data # La variable auxiliar toma el valor de data current.next = None # Del nodo a eliminar (al que sigue apuntando current), se anula la referencia (next) hacia el nodo siguiente; a esto le llamo "eliminar" lo que en realidad seria aislar el nodo. # Si la data a eliminar no coincide con la data de head, entonces ... else: current = current.next # current apunta al nodo siguiente # Verifico si la data a eliminar coincide con la data de alguno de los nodos restantes de la lista while current: if current.data == data: # Si current apunta al nodo cuya data coincide con la data a eliminar ... previous.next = current.next # Entonces el nodo anterior a current tendra la referencia (next) hacia el nodo siguiente a current, de esta forma el nodo al que apunta current se estaría "eliminando" de la lista for node in self.iter(): # Con este ciclo for se logra que tail termine apuntando al último nodo de la lista, sin importar que nodo aya eliminado self.tail = node self.size -= 1 data_deleted = data # La variable auxiliar toma el valor de data current.next = None # Del nodo a eliminar (al que sigue apuntando current), se anula la referencia (next) hacia el nodo siguiente; a esto le llamo "eliminar" lo que en realidad seria aislar el nodo. break # Al declarar "break" se eliminaría solo el primer nodo que coincida con la data a eliminar, esto sirve en caso se tenga más de un nodo con la misma data else: # Si current no apunta al nodo cuya data coincide con la data a eliminar se sigue recorriendo la lista current = current.next # current apunta al nodo siguiente previous = previous.next # previous apunta al nodo siguiente if data_deleted == None: # Si la data la eliminar no coincidió con la data de algún nodo de la lista print(f'Node with data {data} does not exist') else: print(f'Node with data {data_deleted} was deleted') ```
Exelente, no entendi nada. Esto no fue una clase, fue mas bien una demostracion
Curso interesante. pero creo que requiere más detalle en la explicación de las estructuras de datos que intenta construir el profesor.

Voy a buscar más información acerca de eso, porque la verdad entendí bastante poco.

reto: pasar valores de un array a una linked list

  • para ello se usarán los módulos que contienen a las clases: new_Array, Node, Single_linked_list
from clase_5_nodos import Node
from clase_6_linked_list import Singly_linked_list
from clase_3_crear_un_array import new_Array

# array de 5 elementos usando la clase new_Array
array = new_Array(5)  

# agregar valores 
for i in range(1, 6):
    array[i - 1] = i

# nuevo Singly_linked_list
linked_list = Singly_linked_list()

# por medio de un ciclo: Transferimos los vals del array a la linked list
for i in range(len(array)):
    #agregamos el valor de la iteración a un nuevo nodo usando la ƒ "append_nodes"
    linked_list.append_nodes(array[i])

#en otro ciclo, imprimimos sus valores,  iteramos usando la ƒ "iter()"  
for nodo in linked_list.iter():
    print(nodo)

#R: 1,2,3,4,5
from node import Node

class SinglyLinkedList:
    def __init__(self):
        self.tail = None
        self.size = 0

    def append(self, data):
        node = Node(data)

        if self.tail == None:
            self.tail = node
        else:
            current = self.tail

            while current.next:
                current = current.next

            current.next = node

        self.size += 1

    def size(self):
        return str(self.size)

    def iter(self):
        current = self.tail

        while current:
            val = current.data
            current = current.next
            yield val 

    def delete(self, data):
        current = self.tail
        previous = self.tail

        while current:
            if current.data == data:
                if current == self.tail:
                    self.tail = current.next
                else:
                    previous.next = current.next
                    self.size -= 1
                    return current.data

            previous = current 
            current = current.next

    def search(self, data):
        for node in self.iter():
            if data == node:
                print(f"Data {data} found!")

    def clear(self):
        self.tail = None
        self.head = None
        self.size = 0

Crea la clase Node que representará un nodo en la lista enlazada. Cada nodo contendrá un dato y una referencia al siguiente nodo.

Nude.py
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

LinkedList.py

Estructura de la lista enlazada

class LinkedList:
    def __init__(self):
        self.head = None

    def add_element(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node

array.py

Crea un array unidimensional y transfiere sus datos a la lista enlazada.

# Crear el array unidimensional
my_array = [1, 2, 3, 4, 5]

# Crear la lista enlazada
my_list = LinkedList()

# Transferir los datos del array a la lista enlazada
for data in my_array:
    my_list.add_element(data)

el codigo lo transcribio mal el profe que ni lo subio a su github. Naaa, toca buscar por otro lado.

Que clase más horrible

esta fue la forma en que realice el reto

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
numeros = SinglyLinkedList()

for i in nums:
    numeros.append(i)

current = numeros.tail

while current:
    print(current.data)
    current = current.next

Dejo el challenge resuelto 😃 con su output:

"""
Reto:
Crear un array unidimensional.
Tranferir los datos a una linked structure sencilla
"""

from my_array import Array
from linked_list import SinglyLinkedList

# Creo mi Array
my_array = Array(10, 0)
# Imprimo mi array para verificar que esté creado.
# Al imprimir el objeto recurre a su metodo __str__
print(my_array)
print()
# Asigno valores random a mi array:
my_array.__randomitem__()
# Vuelvo a imprimir mi array con valores random
print(my_array)
print()

# Creo mi linked list:
my_linked = SinglyLinkedList()

# Cargo mi linked list con los valores del array:
for value in my_array.__iter__():
    my_linked.append(value)

# Imprimo los valores de mi linked list ya cargada:
for value in my_linked.iter():
    print(value)

print()

"""
23:36:12 👽 with 🤖 mgobea 🐶 in develop/python/data_structs_python …
➜ python3 challenge_single.py
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

[6, 15, 10, 53, 50, 34, 90, 76, 32, 85]

6
15
10
53
50
34
90
76
32
85
"""

Solución para imprimir un mensjae también en el caso donde el nodo buscado no se encuentre en la lista:

    def search(self, data):
        check = True
        for node in self.iter():
            if data == node:
                check = True
            else:
                check = False
        if check:
            print(f"Data: {data} found!")
        else:
            print(f"{data} not found in the list!")

Pésima clase, el profe habla de un head que no existe, bueno lo deja en None en el metodo clear (es decir le sirve para nada), tocó buscar material adicional para recordar bien este tema que por cierto en su momento lo vi en C que era un poco mas complejo

El reto queda así:
Utilicé la clase Array del reto anterior y la clase SinglyLinkedList de la clase para sacarlo:

La clase Array es:

class Array:
    def __init__(self, capacity, fill_value=None):
        self.items = list()
        for i in range(capacity):
            self.items.append(fill_value)

    def __len__(self):
        return len(self.items)

    def __str__(self):
        return str(self.items)

    def __iter__(self):
        return iter(self.items)

    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self, index, new_item):
        self.items[index] = new_item

    def __random_fill__(self):
        import random

        for i in range(len(self.items)):
            self.items[i] = random.randint(i + 1, len(self.items))

    def __secuence_fill__(self):
        for i in range(len(self.items)):
            self.items[i] = i + 1

    def __custom_sum__(self):
        from functools import reduce

        return reduce(lambda a, b: a + b, self.items)

La clase SinglyLinkedList es:

from . node import Node


class SinglyLinkedList:
    def __init__(self):
        self.tail = None
        self.size_list = 0

    def append(self, data):
        node = Node(data)

        if self.tail == None:
            self.tail = node
        else:
            current = self.tail

            while current.next:
                current = current.next

            current.next = node

        self.size_list += 1

    def size(self):
        return self.size_list

    def iter(self):
        current = self.tail

        while current:
            val = current.data
            current = current.next
            yield val

    def delete(self, data):
        current = self.tail
        previous = self.tail

        while current:
            if current.data == data:
                if current == self.tail:
                    self.tail = current.next
                else:
                    previous.next = current.next
                    self.size_list -= 1
                    return current.data
            previous = current
            current = current.next

    def search(self, data):
        found = False
        for node in self.iter():
            if data == node:
                print(f"Data {data} found!")
                found = True

        if  not found:    
            print(f"Data {data} not found!")


    def clear(self):
        self.tail = None
        self.head = None
        self.size_list = 0

    def show(self):
        for node in self.iter():
            print(node)

    


El código del reto:

from linked_lists.linked_list import SinglyLinkedList
from arrays.random_array import Array

if __name__ == '__main__':
    numbers = Array(5)
    numbers.__random_fill__()
    print("The array numbers is: ")
    for i in range(numbers.__len__()):
        print(numbers[i])

    # Transfer data in a single linked list
    list_array = SinglyLinkedList()
    for number in numbers.__iter__():
        list_array.append(number)

    # Print the single linked list
    print("The single linked list is: \n")
    list_array.show()

    


Hola el metodo delete del profesor esta errado, para este metodo es importante seguir o traquear dos nodos, al que le estamos verificando la data y al anterior, por lo que es mas fácil llevar dos variables. Acá les comparto mi código, que me ha funcionado.

    def delete(self, data):
        previous = self.tail
        current = previous.next

        if previous.data == data:
            self.tail = current
            previous = None
            print(f'{data} deleted!')
            return

        while current and current.data != data:
            previous = current
            current = current.next
        
        if current is None:
            print("Data is not in the list")
            return 

        previous.next = current.next
        current = None
        self.size -= 1
        print(f'{data} deleted!')
        return

Recomiendo que Platzi tenga más cursos tratando el tema de las estrucutras de datos de una manera más profunda, explicando el porqué de cada cosa, así se forma un esquema mental lógico de cómo funciona cada estructura.

Me parece que las explicaciones de esta clase — y en general podría decir del curso — son un poco planas. Pueden hacerlo mejor.

Si sienten que necesitan cualquier tipo de documentación alguna vez acerca de estructuras de datos, algoritmos, métodos, funciones, librerías, etc., GeeksforGeeks es una excelente opción.

Links:

  1. GeeksforGeeks
  2. Linked lists

Modificación del método search para el caso de uso cuando no exista un nodo con lo que estamos buscando.
.

    def search(self, data):
        find = False
        data_search = None

        for node in self.iter():
            if node == data:
                find = True
                data_search = node
                break
                
        if find:
            print(f"Element {data_search} found in the list")
        else:
            print(f"Element {data} not found in the list")

Comparto mi solución del reto

from array import Array
from linked_list import SinglyLinkedList

numbers = Array(5)
for counter in range(0,5):
  numbers.__setitem__(counter, counter)

linked_list = SinglyLinkedList()

for counter in range(0,5):
  linked_list.append(numbers.__getitem__(counter))

for link in linked_list.iter():
  print(link)

esta es la solucion al reto:

#Familia(Single Linked List)
    family_list = ["Omar", "Liliana", "Santiago", "Mauricio"]
    family_array = Array(4, family_list)
    familia = SinglyLinkedList()
    for member in family_array.__iter__():
        familia.append(member)

    print(familia.size)
    for member in familia.iter():
        print(member)

ademas de esto adecue la clase Array para que al introducirse una lista la “Descomponga” y de esta manera se cree rápidamente el Array.
Entonces quedo así:

def __init__(self, capacity, fill_value= None):
        self.items = list()
        for i in fill_value:
            self.items.append(i)

Horrible clase

Puse este metodo en la clase the la lista y usando ese decorador el metodo esta asociado a la clase, no a una instancia de esta. no es necesario crear una linked_list para usar el metodo

# Create array_to_singly_linked_list static method
    @staticmethod
    def array_to_singly_linked_list(array):
        result_linked_list = SinglyLinkedList()

        for item in array:
            result_linked_list.append(item)
        return result_linked_list
'''node.py'''

class Node():
    def __init__(self, data, next=None):
        self.data = data
        self.next = next
        
    def __str__(self):
        return str(self.data)

        
if __name__ == '__main__':
    node1 = None
    node2 = Node('A', None)
    node3 = Node('B', node2)
    
    print(node2.data)
    print(node2.next)
    print(node3.data)
    print(node3.next)
    print(node3.next.data)
    node1 = Node('C', node3)
    print(node1.data)
    print(node1.next.data)
    
    head = None
    for count in range(1, 5):
        head = Node(count, head)
        
    print(f'El objeto Node que quedo referenciada en head es Nodo{head.data}, pero este apunta al Nodo{head.next.data}este a su vez apunta al Nodo{head.next.next.data}, y este ultimo a apunta al Nodo{head.next.next.next.data}')
    
    while head != None:
        print(head.data)
        head = head.next
    
    node2
'''linked_list.py'''

class SinglyLinkedList:
    def __init__(self):
        self.tail = None
        self.size = 0
    
    def append(self, data):   
        node = Node(data)
        
        if self.size == 0:
            self.tail = node
        else:
            current = self.tail
        
            while current.next != None:
                current = current.next
            
            current.next = node
        
        self.size += 1 
        
        return print(f'Se ha añadido el nodo "{node}"')
        
    def size(self):
        return str(self.size)
    
    def iter(self):
        current = self.tail
        
        while current:
            val = current.data
            current = current.next
            yield val
            
    def delete(self, data):
        if self.size ==0:
            return False
        else:
            current = self.tail
            previous = self.tail

            while current:
                if current.data == data:
                    if current == self.tail:
                        self.tail = current.next
                    else:
                        previous.next = current.next
                        self.size -= 1
                        return current.data

                previous = current
                current = current.next
    
    def search(self, data):
        for node in self.iter():
            if data == node:
                print(f'Data {data} found!')
                
    def clear(self):
        self.tail = None
        self.head = None
        self.size = 0
        
    def __str__(self):
        string = '['
        current = self.tail
        for i in range(self.size):
            string += str(current)
            if i != self.size - 1:
                string += str(', ')
            current = current.next
        string += ']'
        
        return string

if __name__ == '__main__':
    words = SinglyLinkedList()
    words.delete('1')
    words.append('egg')
    words.append('ham')
    words.append('spam')
    words.delete('ham')
    print(words.__str__())
    print(words)
    
    current = words.tail
    
    while current:
        print(current.data)
        current = current.next
    
    a = [word for word in words.iter()]
    print(a)
        
    words.search('spam')
    words.clear()
    
    while current:
        print(current.data)
        current = current.next

Algo para resaltar es que la propiedad next está apuntando a un objeto que ya existe en memoria y por tanto expone la dirección de memoria, y no explícitamente a una dirección de memoria.

En este clase explican estructuras de datos, entre ellas la Linked List. Me ayudo a entender mejor como funciona y como implementar algunas de sus propiedades.

CREA ARRAY Y LO PASA A UN LINKED SIMPLE STRUCTURE (EN EL ORDEN DE CREACION)

from c_array import Array
from node import Node

def main():
n = int(input("Ingrese un largo de Array: "))
arr = Array(n)
for i in range(len(arr)):
arr.items[i] = i
print(arr)

head = None
for i in range(len(arr)):
    if head == None:
        nodo = Node(arr.items[i])
        head = nodo
        tail = nodo
    else:
        nodo = Node(arr.items[i])
        tail.next = nodo
        tail = nodo
nodo = head
while nodo:
    print(nodo.data)
    nodo = nodo.next
print("--o--")
print("Head", " ", head.data)
print("Tail", " ", tail.data)

if name == ‘main’:
main()

SALUDOS FRIEND…!

Esta fue mi solución al reto :3

from linked_list import SinglyLinkedList

def run():
    array = [1, 2, 3, 4, 5]
    linkedList = SinglyLinkedList()

    for item in array:
        linkedList.append(item)
    
    for item in linkedList.iter():
        print(item)


if __name__ == '__main__':
    run()

Reto. Creo que para practicidad y un mejor entendimiento de la clase, se debe cambiar tail por head (de todas formas dejo mi tarea con base a lo que dejo el profe).

from nodes import Node
from class_array import Array
from random import randint

class singleLinkedList():
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0
    ## IMPORTANT: FROM THIS PART, TAIL ACTS LIKE A HEAD
    def appe(self, data): #agregar datos desde el tail
        new_node = Node(data)
        if self.tail == None:
            self.tail = new_node #si la lista esta vacía
        else:
            current = self.tail
            while current.next: #forma de recorrer la lista
                current = current.next
            current.next = new_node #donde esta el 'None' colocar el nuevo nodo
        self.size += 1 #aumenta de tamaño la lista
    
    def takeFromArray(self, array=list()):# o una instancia de array
        for i in array:
            self.appe(i)

    def size(self):
        return str(self.size)

    def iter(self):
        current = self.tail
        while current:
            val = current.data
            current = current.next
            yield val

    def printList(self):
        current = self.tail
        print('Data in Single Linked List:', end='\n')
        while current is not None:
            print(current.data, end=" ")
            current = current.next
        print('')

    def delete(self, data):
        current = self.tail #current y previous definen los datos que estrán en medio del que se va a eliminar
        previous = self.tail
        while current:
            if current.data == data:
                if current == self.tail:
                    self.tail = current.next #si el dato a eliminar esta de primero
                else:
                    previous.next = current.next
                    self.size -= 1
                    return current.data
            previous = current
            current = current.next
    
    def search(self, data):
        count = 0
        for node in self.iter():
            count += 1
            if data == node:
                print (f'Data {data} found!')
                break
            elif count == self.size:
                print ('Data not found!')

    def clear(self):
        self.head = None
        self.tail = None
        self.size = 0

if __name__ == '__main__':

  LL = singleLinkedList()
    LL.takeFromArray(Array(5))#o una lista normal
    LL.printList()

Tras terminar la clase me parece que self.tail en realidad es self.head ya que esta siempre apunta al primer nodo creado.

Por si alguien más tuvo esa duda al interpretar el código.

Así complementé la función de busqueda, cuando no encuentra el elelemento.

def search(self, data):
        for node in self.iter():
            if data == node:
                print(f'Data {data} found! :D')
                break

        if data not in self.iter():
            print(f'Data {data} not found! :c')

En los archivos de las clases el código de singly linked list no está. En su lugar esta el de la class Node

Este es mi aporte: Asi quedo mi codigo de modo que podemos aplicar todas las demas funciones

from node import Node


class SinglyLinkedList:
    def __init__(self):
        self.tail = None
        self.tamaño = 0


    def append(self, data):
        node = Node(data)

        if self.tail == None:
            self.tail = node
        else:
            current = self.tail

            while current.next:
                current = current.next

            current.next = node
            self.tamaño +=1


    def insert_value_from_list(self, data_list):
        """Agrega los datos de una lista al LinkedList"""
        for data in data_list:
            self.append(data)
        current = datos.tail

        while current:
            print(current.data)
            current = current.next


    def size(self):
        print("The size of the LinkedList is:")
        return str(self.tamaño)


    def iter(self):
        current = self.tail

        while current:
            val = current.data
            current = current.next
            yield val


    def delete(self, data):
        current = self.tail
        prev =current

        while current:
            if current.data == data:
                if current == self.tail:
                    self.tail = current.next
                else:
                    prev.next = current.next
                    self.tamaño -= 1
                return  print(f"The {current.data} was delete")
            else:
                prev = current
                current = current.next
        print(f"The {data} was not found to delete")


    def search(self, data):
        for node in self.iter():
            if data == node:
                print(f"The {data} was found!")


    def display_list(self):
        """Muestra los datos del LinkedList"""
        for node in self.iter():
            print(node)


    def clear(self):
        self.tail = None
        self.head = None
        self.tamaño = 0


if __name__ == "__main__":

    zoo = ["oso","gato","perro","lion"]
    datos = SinglyLinkedList()

    print("LinkedList...")
    datos.insert_value_from_list(zoo)
    print("\n")
    print("Borrando elemento de la LinkedList (perro)")
    datos.delete("perro")
    print("Mostrando lo que queda de la LinkedList...\n")
    datos.display_list()

Basicamente esto va pasando.

none
Node#1(value, none)
Node#2(value, Node#1(value, none))
Node#3(value, Node#2(value, Node#1(value, none)))
Node#4(value, Node#3(value, Node#2(value, Node#1(value, none))))

con eso mas claro, al inicio lo entendi al reves jajaja

Mi solucion al reto sin añadirlo como un metodo de SinglyLinkedList

def run():
    random_int = Array(5)
    random_int.__fillRandom__()
    print(random_int)
    
    random_int_linked = SinglyLinkedList()

    print("Creating the random_int_linked")
    for num in random_int.__iter__():
        random_int_linked.append(num)

    print("Printing all the created nodes in the linked list")
    for random in random_int_linked.iter():
        print(random)

if __name__ == "__main__":
        run()

Resultado en terminal

[0, 7, 10, 2, 10]
Creating the random_int_linked
Printing all the created nodes in the linked list
0
7
10
2
10

Hago mi aporte al reto, usando la clase Array vista en clases anteriores:

from node import Node
from array import Array


class SinglyLinkedList:
    def __init__(self):
        self.tail = None 
        self.size = 0 


    def append(self, data):
        node = Node(data) 

        if self.tail == None:
            self.tail = node 
        else:
            current = self.tail

            while current.next:
                current = current.next

            current.next = node

        self.size += 1


    def size(self):
        return str(self.size)


    def iter(self):
        current = self.tail

        while current:
            val = current.data 
            current = current.next 
            yield val 
        
    
    def delete(self, data):
        current = self.tail 
        previous = self.tail 

        while current:
            if current.data == data:
                if current == self.tail:
                    self.tail = current.next 
                else:
                    previous.next = current.next 
                    self.size -= 1 
                    return current.data

            previous = current 
            current = current.next 


    def search(self, data):
        for node in self.iter():
            if data == node:
                print(f'Data {data} found!')


    def turn_array_to_linked_list(self):
        # Método para pasar un array a lista enlazada
        array_numbers = Array(5)
        array_numbers.__addrandomitems__()

	# imprimir el array para observar los números generados de forma aleatoria
        print(array_numbers)

        for number in array_numbers:
            self.append(number)



    def clear(self):
        self.tail = None
        self.head = None
        self.size = 0

🚩🚩🚩🚩🚩🚩

Es mucho más “cómodo” y “simple” manejar un 3er atributo en la clase => head

Mi solución al reto:

from my_array import Array
from linked_list import SinglyLinkedList


def main():
    new_array = Array(5)
    new_array.set_values(80,13)

    linked_list = SinglyLinkedList()

    for i in new_array.__iter__():
        linked_list.append(i)

    print(new_array)

    for i in linked_list.iter():
        print(i)


if __name__ == '__main__':
    main()

https://www.youtube.com/watch?v=oXuKUkIlv_o en este video de youtube pude comprender un poco mejor y de forma mas gráfica la singly linked list

Solucion al reto

from Arrays.arrays import Array
from linked_lists.singly_list import SinglyLinkedList as SLL


def run():
    array = Array(10)
    list = SLL()
    array.rand_fill(20, 1000)
    for i in array:
        list.append(i)
    print(list)


if __name__ == '__main__':
    run() 

Output:

465, 546, 294, 172, 308, 561, 252, 83, 185, 482

Process finished with exit code 0

Hay un error en el metodo delete y es que si elimina el elemento de la cabeza de la lista no retorna ningun valor y tampoco disminuye el tamaño de la lista lo correcto seria de la siguiente forma

while current is not None:
    if current.data == instance:
        if current == self.head:
            self.head = current.next
        else:
            previous.next = current.next
        self.size -= 1
        return current.data

    previous = current
    current = current.next

Solución al Reto

from array import Array
from linked_list import SinglyLinkedList

if __name__ == '__main__':
    mi_array = Array(5)

    mi_array.__fillrandom__()
    print(mi_array)

    mi_linked_list = SinglyLinkedList()

    for i in range(mi_array.__len__()):
        mi_linked_list.append(mi_array.__getitem__(i))

    for item in mi_linked_list.iter():
        print(item)

Espero les ayude.
Reto:

from arreglo import Arreglo
from node import Node
from linked_list import SinglyLinkedList

size = 5
#.__replaceitems__() Función para replazar los valores del arreglo por numeros aleatorios
lista = Arreglo(size).__replaceitems__()
print(lista)
nodes = SinglyLinkedList()
for i in range(size):
    nodes.append(data=lista[i])

current = nodes.tail
while current:
    print(current.data)
    current = current.next

Salida:

[80, 37, 49, 80, 52]
80
37
49
80

Reto:

from MiArray import Array
from linked_list import SinglyLinkedList

def run():
    array = Array(6)
    array.__populate__(0, 100)
    print(array)
    node = SinglyLinkedList()
    for data in array.__iter__():
        node.append(data)

    del array

    current = node.tail
    while current:
        print(current.data)
        current = current.next

if __name__ == '__main__':
    run()

Anotación: el metodo setvalues se creo en el reto de llenar el array con numeros random

from Array import Array
from linked_list import SinglyLinkedList

list = Array(5)

list.__setvalues__()
print(list.__str__())

data = SinglyLinkedList()

for i in list:
    data.append(i)
current = data.tail

while current:
    print(current.data)
    current = current.next

pienso que me gustaría que las armara desde el principio y no utilizara cosas ya hechas, ya se que no hay que reinventar la rueda, pero me siento incompleto, alguien tiene material para complementar?

Agregar por indice:

Borrar un dato segun su indice:

Una forma para insertar data desde atras:

Aporte | Reto

Les comparto mi segunda versión del reto, después de un tiempo de dejar abandonado el curso 😅

Hice los features para que la linked list pueda manipularse bajo las mismas características de un list.

Uso

from linked_list import SinglyLinkedList

# Se puede crear una linked list apartir de list, set o tuple.
singly_linked_list = SinglyLinkedList([1, 2, 3, 4, 5])  # 1 -> 2 -> 3 -> 4 -> 5

# Podemos acceder a los elementos de la linked list a traves de sus indices.
linked_list_slice = singly_linked_list[0:-2]  # 1 -> 2 -> 3 -> 4

print(singly_linked_list[3])  # 4

for item in singly_linked_list[::2]:
    print(item)  # 1 -> 3 -> 5


# Comparación de listas
linked_list_2 = SinglyLinkedList(1, 2, 3, 4)

print(singly_linked_list == linked_list_2)  # False
print(singly_linked_list >= linked_list_2)  # True
print(singly_linked_list <= linked_list_2)  # False
print(singly_linked_list > linked_list_2)  # True
print(singly_linked_list < linked_list_2)  # False

Código

from pdb import set_trace
from typing import Any
from typing import Optional
from typing import Iterator
from typing import Union

from node import Node


class SinglyLinkedList:
    """Singly Linked List"""

    _tail: Optional[Node]
    _size: int

    def __init__(self, *items) -> None:
        """Initialize an empty list

        Args:
            *items: Items to be added to the list.
        """

        self._head = None
        self._size = 0

        if len(items) == 1 and isinstance(items[0], (list, tuple, set)):
            items = items[0]

        for item in items:
            self.append(item)

    def append(self, value: Any):
        """Appends a new value at the end of the list.

        Time complexity: O(n) where n is the length of the nodes.
        """

        node = Node(value)

        if self._head is None:
            self._head = node

        else:

            pointer = self._head

            while pointer.next is not None:
                pointer = pointer.next

            pointer.next = node

        self._size += 1


    @property
    def size(self):
        """Returns the size of the linked list.

        Time complexity: O(1)
        """

        return self._size

    def delete(self, target: object):
        """Deletes an item from the list.

        Time complexity: O(n)
        """

        current = self._head
        previous = current

        while current is not None:
            if current.value == target:
                if current == self._head:
                    self._head = current.next
                else:
                    previous.next = current.next

                self.size -= 1
                break

            previous = current
            current = current.next

    def search(self, target: object) -> bool:
        """Check if the provided value is in the list.

        Time complexity: O(n) where n is the length of the nodes.

        Args:
            target: Value to be searched inside the list.

        Returns:
            bool: True if found. False otherwise.
        """

        return target in self

    def clear(self):
        """Clears the list.

        Removes all the items inside the list.
        """

        self._head = None
        self.size = 0

    def __len__(self):
        """Returns the size of the linked list when using the built-in function len."""
        return self._size

    def __iter__(self) -> Iterator[Node]:
        """Allows to iterate over the list using a generator.

        Time complexity: O(n)
        """

        if self._head is not None:
            pointer = self._head

            while pointer is not None:
                val = pointer
                pointer = pointer.next

                yield val

        return None

    def __contains__(self, target: object) -> bool:
        """Check if a value is in the list when using the 'in' operator.

        Time complexity: O(n) where n is the length of the nodes.
        """

        output = False

        pointer = self._head

        while pointer is not None and pointer.value != target and pointer.next is not None:
            pointer = pointer.next

        if pointer is not None and pointer.value == target:
            return target

        return output

    def __reversed__(self) -> Iterator[Node]:
        """Returns a new instance of the linked list but reversed when using the 'reversed' built-in function.

        Time complexity: O(n) where n is the length of the nodes.
        """

        new_list = SinglyLinkedList()

        if self._head is not None:

            current_node: None = self._head
            remaining_values: Optional[Node] = self._head.next

            # Set the head next value to None beacuse now it will be the tail
            current_node.next = None

            while remaining_values is not None:

                temp_ref = current_node
                current_node: Node = remaining_values
                remaining_values = current_node.next

                current_node.next = temp_ref

            new_list._head = current_node

        return new_list

    def __getitem__(self, index: Union[int, slice]):
        """Get item with the index or slice from the linked list.

        Time Complexity: O(n) where n is the length of the nodes.

        Returns:
            Any: Item with the index or slice.
        """

        if isinstance(index, slice):

            items = list(self)[index]
            new_list = SinglyLinkedList(items)

            return new_list

        if index > self.size - 1:
            raise IndexError('Index out of range')

        if index < 0:
            index = self._size + index

        for idx, value in enumerate(self):
            if index == idx:
                return value

        return None


    def __lt__(self, other: 'SinglyLinkedList') -> bool:
        """Check if the list is less than the other list.

        Time Complexity: O(n) where n is the length of the nodes of the list with less nodes.

        Args:
            other (SinglyLinkedList): Other list to be compared.

        Returns:
            bool: True if the list is equals to the other list. False otherwise.
        """

        if self.size >= other.size:
            return False


        for self_item, other_item in zip(self, other):
            if self_item.value != other_item.value:
                return False

        return True

    def __le__(self, other: 'SinglyLinkedList') -> bool:
        """Check if the list is less than or equals to the other list.

        Time Complexity: O(n) where n is the length of the nodes of the list with less nodes.

        Args:
            other (SinglyLinkedList): Other list to be compared.

        Returns:
            bool: True if the list is equals to the other list. False otherwise.
        """

        if self.size > other.size:
            return False

        for self_item, other_item in zip(self, other):
            if self_item.value != other_item.value:
                return False

        return True

    def __eq__(self, other: 'SinglyLinkedList') -> bool:
        """Check if the list is equal to the other list.

        Time Complexity: O(n) where n is the length of the nodes of the list with less nodes.

        Args:
            other (SinglyLinkedList): Other list to be compared.

        Returns:
            bool: True if the list is equals to the other list. False otherwise.
        """

        if self._size != other.size:
            return False

        for self_item, other_item in zip(self, other):
            if self_item.value != other_item.value:
                return False

        return True


    def __ge__(self, other: 'SinglyLinkedList') -> bool:
        """
        Check if the list is greater or equal than the other list.

        Time Complexity: O(n) where n is the length of the nodes of the list with less nodes.

        Args:
            other (SinglyLinkedList): Other list to be compared.

        Returns:
            bool: True if the list is greater or equal than the other list. False otherwise.
        """

        if self._size < other._size:
            return False

        for self_item, other_item in zip(self, other):
            if self_item.value != other_item.value:
                return False

        return True

    def __gt__(self, other: 'SinglyLinkedList') -> bool:
        """
        Check if the list is greater than the other list.

        Time Complexity: O(n) where n is the length of the nodes of the list with less nodes.

        Args:
            other (SinglyLinkedList): Other list to be compared.

        Returns:
            bool: True if the list is greater than the other list. False otherwise.
        """

        if self.size <= other.size:
            return False

        for self_item, other_item in zip(self, other):
            if self_item.value != other_item.value:
                return False

        return True

Propuesta anterior: https://platzi.com/comentario/2616441/

Reto…

from linkedList import SinglyLinkedList
from customArray import Array

def arrayToList(array: Array) -> SinglyLinkedList:
    linkedList = SinglyLinkedList()

    for item in iter(array):
        linkedList.append(item)
    return linkedList


def run():
    array = Array(10) #Crates an Array object
    array.randomitems(0,10) # Fill the array with random int numbers between 0 ans 10
    print(str(array)) # Prints the str format representation of the array
    numbers = arrayToList(array) #Turn the array into a SinglyLinkedList
    numbers.str() # Print the str format representation of the SinglyLinkedList

if __name__=="__main__":
    run()

Implementación del método str de la clase SinlyLinkedList

    def str(self):
        current = self.tail
        items = ""

        while current:        
            items += str(current.data) + " "
            current = current.next
        print(items)

Implementación de caso de uso para def search(self, data)
Para en caso de no encontrar el elemnto.

    def search(self, data):
        found=False
        for node in self.iter():
            if data == node:
                print(f'Data: "{data}" found!')
                found = True
        if found==False:
            print(f'Sorry...Data: "{data}" NOT found!')   

Reto:

# Reto
    print("RETO")
    values = [i for i in range(1, 15)]
    values = [Node(i) for i in values]
    numbers = SingleLinkedList()
    for value in values:
        numbers.append(value.data)
    for number in numbers.iter():
        print(number)
    print(f"size {numbers.list_size()=}")
    print(numbers.search(5))
    print(numbers.search("potato"))

Una buena forma de usar un format-string para ver los datos es:

data = 'spam'
print(f"{data=} found)
# imprime
data='spam' found

Mi implementación:

Mi código de la clase, hice algunas cosas ligeramente distintas:

from node import Node


class SingleLinkedList:
    """Create a Single Linked List """

    def __init__(self):
        """Initialize a single linked list"""

        self.head = None
        self.tail = None
        self.size = 0

    def append(self, data):
        """Add a new node to the end of the list"""

        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
        else:
            self.tail.next = new_node
            self.tail = new_node
        self.size += 1

    def size(self):
        """Return the size of the list"""

        return str(self.size)

    def iter(self):
        """Iterate over the list"""

        current = self.head
        while current:
            yield current
            current = current.next

    def delete(self, data):
        """Delete a node from the list"""

        current = self.head
        previous = None
        found = False
        while current and found is False:
            if current.data == data:
                found = True
            else:
                previous = current
                current = current.next
        if current is None:
            raise ValueError("Data not in list")
        if previous is None:
            self.head = current.next
        else:
            previous.next = current.next
        self.size -= 1

    def search(self, data):
        """Search for a node in the list"""

        current = self.head
        found = False
        while current and found is False:
            if current.data == data:
                print(f'Data {data} was found')
                found = True
            else:
                current = current.next
        if current is None:
            raise ValueError("Data not in list")
        return current

    def clear(self):
        """Clear the list"""
        self.head = None
        self.tail = None
        self.size = 0


if __name__ == '__main__':

    sll = SingleLinkedList()
    sll.append(1)
    sll.append('hey')
    sll.append(3.14)
    sll.append('Hola')
    current = sll.tail

    for node in sll.iter():
        print(node.data)

    print(sll.size)
    sll.search('hey').data
    sll.search('545').data

Aqui cumpliendo con el reto

mi version de search

def search(self, data):
        result = 'not found'
        for node in self.iter():
            if data == node:
                result = 'found' 
        print(f'Data {data} {result}!!')

Mi solución al reto:
.
.

Aporte | Reto

Les comparto mi solución del problema, traté de hacer las mejores soluciones que se me ocurrieron al momento basado en la teoría de la complejidad computacional.


PD: El método de delete no me termina de gustar, me gustaría saber si tienen alguna sugerencia

Código

class Node:
    def __init__(self, value, next = None) -> None:
        self.value = value
        self.next = next


class SinglyLinkedList:
    """Singly Llinked list."""

    def __init__(self) -> None:
        """Initalize the linked list.
        
        Time complexity: O(1)
        """
        self._head = None
        self._tail = None
        self._size = 0

    @property
    def size(self):
        return self._size

    def append(self, value):
        """Adds a new item to the current linked list instance.

        Time complexity: O(1)
        """

        node = Node(value)

        if self._head is None:
            self._head = node

        elif self._tail is None:
            self._tail = Node(value)
            self._head.next = self._tail
        else:
            self._tail.next = Node(value)
            self._tail = self._tail.next

        self._size += 1

    def iter(self):
        """Returns an iterator object of the current instance.
        
        Time complexity: O(n)
        """
        currentRef = self._head

        while currentRef is not None:
            value = currentRef.value
            currentRef = currentRef.next
            yield value

    def __iter__(self):
        """Returns an iterator object.
        
        Time complexity: O(n)
        """
        return self.iter()

    def delete(self, data):
        """Removes the node that matches with the provided data.
        
        Time complexity: O(n)
        """

        deleted = False
        currentRef = self._head
        previousRef = None

        while currentRef is not None:
            if currentRef.value == data:
                if previousRef is not None:
                    previousRef.next = currentRef.next
                else:
                    self._head = currentRef.next

                deleted = True
                self._size -= 1
                break

            previousRef = currentRef
            currentRef = currentRef.next

        # Find the new tail
        currentRef = self._head
        while currentRef is not None and currentRef.next is not None:
            currentRef = currentRef.next
        self._tail = currentRef

        return deleted

    def search(self, value) -> bool:
        """Returns True if the provided value is found in the current linked list instance.
        
        Time complexity: O(n)
        """

        result = False

        for item in self:
            if item == value:
                result = True
                break
        return result

    def clear(self):
        """Resets the current linked list instance.
        
        Time complexity: O(1)
        """
        self._size = 0
        self._head = None
        self._tail = None

    @staticmethod
    def from_array(source: list):
        """Creates a new instance of a singly linked list from a simple python list.
        
        Time complexity: O(n)
        """

        _list = SinglyLinkedList()

        for item in source:
            _list.append(item)
        
        return _list

    def __str__(self) -> str:
        return repr(list(self))

    def __repr__(self) -> str:
        return str(self)