¿Cómo puedo usar una lista enlazada en Python?

14 minutos de lectura

Avatar de usuario de Claudiu
claudio

¿Cuál es la forma más fácil de usar una lista vinculada en Python? En Scheme, una lista enlazada se define simplemente por '(1 2 3 4 5).

listas de Python, [1, 2, 3, 4, 5]y tuplas, (1, 2, 3, 4, 5), no son, de hecho, listas enlazadas, y las listas enlazadas tienen algunas buenas propiedades, como la concatenación en tiempo constante y la posibilidad de hacer referencia a partes separadas de ellas. ¡Hazlos inmutables y es muy fácil trabajar con ellos!

  • Esto te puede ayudar a visualizarlo… pythontutor.com/…

    usuario1889082

    9 de diciembre de 2012 a las 7:41

  • @ usuario1889082 impresionante! realmente me ayuda a entender algunos conceptos de python

    – Jerry An

    19 de diciembre de 2020 a las 14:19

Avatar de usuario de Ber
Ber

Para algunas necesidades, un deque también puede ser útil. Puede agregar y eliminar elementos en ambos extremos de una deque a un costo de O (1).

from collections import deque
d = deque([1,2,3,4])

print d
for x in d:
    print x
print d.pop(), d

  • Tiempo deque es un tipo de datos útil, no es una lista enlazada (aunque se implementa utilizando una lista doblemente enlazada en el nivel C). Entonces responde a la pregunta “¿qué usarías en lugar de listas vinculadas en Python?” y, en ese caso, la primera respuesta debería ser (para algunas necesidades) una lista ordinaria de Python (tampoco es una lista vinculada).

    – jfs

    19/10/2013 a las 20:26


  • @JFSebastian: Casi estoy de acuerdo contigo 🙂 Creo que la pregunta que responde es más bien: “¿Cuál es la forma pitónica de resolver un problema que usa una lista vinculada en otros idiomas”. No es que las listas enlazadas no sean útiles, es solo que los problemas en los que un deque no funciona son muy raros.

    – Emil Stenström

    20/10/2013 a las 20:46


  • No tiene nada que ver con “Pythonic”: una lista enlazada es una estructura de datos diferente a una deque, y en las diversas operaciones que admiten los dos, tienen diferentes tiempos de ejecución.

    – Tánatos

    7 abr 2014 a las 20:48

  • @dimo414: las listas vinculadas generalmente prohíben la indexación (no linked_list[n]) porque sería O(n). Los dequeues lo permiten y lo ejecutan en O(1). Sin embargo, las listas enlazadas, dado un iterador en la lista, pueden permitir la inserción y eliminación de O(1), mientras que deques no (es O(n), como un vector). (Excepto al principio y al final, donde tanto los deques como las listas enlazadas son O(1). (aunque es probable que el deque esté amortizado como O(1). La lista enlazada no lo está).

    – Tánatos

    19 de julio de 2014 a las 6:39

  • @MadPhysicist “Eso [deque] se comporta como una lista enlazada en casi todos los sentidos, incluso si el nombre es diferente”. — es incorrecto o no tiene sentido: es incorrecto porque las listas enlazadas pueden proporcionar diferentes garantías para las complejidades del tiempo, por ejemplo, puede eliminar un elemento (posición conocida) de una lista enlazada en O(1) mientras que deque no lo promete (no es O(n)). Si “casi todas las formas” permite ignorar la diferencia en O grande, entonces su declaración no tiene sentido porque podríamos usar una lista incorporada de Python como una deque si no fuera por las garantías pop (0), insert (0, v) O grande .

    – jfs

    13/09/2016 a las 11:20


Avatar de usuario de Nick Stinemates
Nick Stinemates

esto lo escribi el otro dia

#! /usr/bin/env python

class Node(object):
    def __init__(self):
        self.data = None # contains the data
        self.next = None # contains the reference to the next node


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

    def add_node(self, data):
        new_node = Node() # create a new node
        new_node.data = data
        new_node.next = self.cur_node # link the new node to the 'previous' node.
        self.cur_node = new_node #  set the current node to the new one.

    def list_print(self):
        node = self.cur_node # cant point to ll!
        while node:
            print node.data
            node = node.next



ll = LinkedList()
ll.add_node(1)
ll.add_node(2)
ll.add_node(3)

ll.list_print()

  • ¿Cómo podría revisar la lista y buscar un nodo específico con datos específicos?

    – loco

    25 de agosto de 2011 a las 17:17

  • @locoboy el código para hacer eso sería similar en lógica al código en list_print().

    -Dennis

    11 de diciembre de 2013 a las 19:28


  • Muestra la lista en orden inverso

    usuario9652688

    20 oct 2019 a las 0:50

avatar de usuario de jfs
jfs

Aquí hay algunas funciones de lista basadas en la representación de Martin v. Löwis:

cons   = lambda el, lst: (el, lst)
mklist = lambda *args: reduce(lambda lst, el: cons(el, lst), reversed(args), None)
car = lambda lst: lst[0] if lst else lst
cdr = lambda lst: lst[1] if lst else lst
nth = lambda n, lst: nth(n-1, cdr(lst)) if n > 0 else car(lst)
length  = lambda lst, count=0: length(cdr(lst), count+1) if lst else count
begin   = lambda *args: args[-1]
display = lambda lst: begin(w("%s " % car(lst)), display(cdr(lst))) if lst else w("nil\n")

donde w = sys.stdout.write

Aunque las listas doblemente enlazadas son famosamente utilizadas en el libro de Raymond Hettinger receta ordenadalas listas enlazadas individualmente no tienen ningún valor práctico en Python.

He Nunca usó una lista enlazada individualmente en Python para cualquier problema, excepto educativo.

Thomas Watnedal sugirió un buen recurso educativo Cómo pensar como un científico informático, Capítulo 17: Listas enlazadas:

Una lista enlazada es:

  • la lista vacía, representada por Ninguno, o
  • un nodo que contiene un objeto de carga y una referencia a una lista enlazada.

    class Node: 
      def __init__(self, cargo=None, next=None): 
        self.car = cargo 
        self.cdr = next    
      def __str__(self): 
        return str(self.car)
    
    def display(lst):
      if lst:
        w("%s " % lst)
        display(lst.cdr)
      else:
        w("nil\n")
    

  • Usted dice: nunca ha usado una lista enlazada individualmente en Python para ningún problema, excepto educativo. Eso es bueno para usted 🙂 Pero puedo asegurarle: HAY problemas en el mundo real donde una lista vinculada proporcionará una solución ideal 🙂 Es por eso que busqué listas vinculadas en StackOverflow en primer lugar 🙂

    – Regis mayo

    27/01/2017 a las 21:30


  • @RegisMay: ¿le importaría proporcionar un enlace a un ejemplo de código práctico específico? (nota: debe ser “una lista enlazada individualmente en Python” “En el mundo real”: describa los beneficios para su ejemplo, por ejemplo, legibilidad, rendimiento u otro “valor práctico” de su elección). Hice una solicitud similar en el pasado: en 8 años, cero enlaces, excepto las listas doblemente enlazadas que se usan en la receta del conjunto ordenado de Raymond Hettinger; tal vez, podría explicarse que solo los programadores nuevos en Python leen esta pregunta: su aporte Sería valioso y muy apreciado.

    – jfs

    27 de enero de 2017 a las 22:02


  • Oh, lo siento. No soy un hablante nativo de inglés y confundí “una lista de enlaces únicos” con “una lista de enlaces únicos”. Sin embargo, necesito una lista enlazada (doble), que no existe en python. Un deque no ayuda, ya que necesito acceso directo a cada elemento individual sin iterar sobre todos los elementos. Mi objetivo: quiero implementar un caché. Sin embargo: si mi imperfección en el idioma inglés hace que mis comentarios estén fuera de lugar, elimine estos comentarios. Perdón por cualquier inconveniente.

    – Regis mayo

    29/01/2017 a las 19:00

  • Una ventaja práctica de una lista enlazada individualmente sobre las listas o matrices doblemente enlazadas (que Python usa internamente para las listas) es que dos listas enlazadas pueden compartir una cola. Esto es muy útil para algoritmos dinámicos que requieren valores guardados de iteraciones anteriores donde compartir colas de listas puede reducir la complejidad de la memoria de cuadrática a lineal y eliminar la sobrecarga de tiempo debido a la copia.

    – Saolof

    25 de junio de 2017 a las 12:08

  • Ese enlace de rosettacode estaba un ejemplo del mundo real, que utiliza una lista vinculada simulada en lugar de una lista vinculada real. Mírelo, reescríbalo para usar una lista enlazada real, para mejorar la claridad y la legibilidad, y ahí tiene el ejemplo del mundo real de una lista enlazada que se usa para mejorar el código existente. Y, en segundo lugar, el algoritmo de subsecuencia creciente más largo se usa en el mundo real, en estadísticas, así que ahí lo tienen. QED :). Más allá de eso, aceptemos estar en desacuerdo. 🙂

    – Gino

    26/09/2017 a las 21:06

Avatar de usuario de Chris Redford
cris redford

La respuesta aceptada es bastante complicada. Aquí hay un diseño más estándar:

L = LinkedList()
L.insert(1)
L.insert(1)
L.insert(2)
L.insert(4)
print L
L.clear()
print L

es un sencillo LinkedList clase basada en el sencillo diseño de C++ y Capítulo 17: Listas enlazadassegún lo recomendado por Thomas Watnedal.

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

    def __str__(self):
        return 'Node ['+str(self.value)+']'

class LinkedList:
    def __init__(self):
        self.first = None
        self.last = None

    def insert(self, x):
        if self.first == None:
            self.first = Node(x, None)
            self.last = self.first
        elif self.last == self.first:
            self.last = Node(x, None)
            self.first.next = self.last
        else:
            current = Node(x, None)
            self.last.next = current
            self.last = current

    def __str__(self):
        if self.first != None:
            current = self.first
            out="LinkedList [\n" +str(current.value) +'\n'
            while current.next != None:
                current = current.next
                out += str(current.value) + '\n'
            return out + ']'
        return 'LinkedList []'

    def clear(self):
        self.__init__()

Las listas inmutables se representan mejor a través de dos tuplas, con None representando NIL. Para permitir una formulación simple de tales listas, puede usar esta función:

def mklist(*args):
    result = None
    for element in reversed(args):
        result = (element, result)
    return result

Para trabajar con tales listas, prefiero proporcionar la colección completa de funciones LISP (es decir, primera, segunda, enésima, etc.), que introducir métodos.

Aquí hay una versión un poco más compleja de una clase de lista enlazada, con una interfaz similar a los tipos de secuencia de python (es decir, admite indexación, división, concatenación con secuencias arbitrarias, etc.). Debe tener O (1) antepuesto, no copia datos a menos que sea necesario y se puede usar de manera bastante intercambiable con tuplas.

No será tan eficiente en el espacio o el tiempo como las celdas de cons de lisp, ya que las clases de python obviamente son un poco más pesadas (podría mejorar las cosas ligeramente con “__slots__ = '_head','_tail'” para reducir el uso de memoria). Sin embargo, tendrá las características de rendimiento deseadas de gran O.

Ejemplo de uso:

>>> l = LinkedList([1,2,3,4])
>>> l
LinkedList([1, 2, 3, 4])
>>> l.head, l.tail
(1, LinkedList([2, 3, 4]))

# Prepending is O(1) and can be done with:
LinkedList.cons(0, l)
LinkedList([0, 1, 2, 3, 4])
# Or prepending arbitrary sequences (Still no copy of l performed):
[-1,0] + l
LinkedList([-1, 0, 1, 2, 3, 4])

# Normal list indexing and slice operations can be performed.
# Again, no copy is made unless needed.
>>> l[1], l[-1], l[2:]
(2, 4, LinkedList([3, 4]))
>>> assert l[2:] is l.next.next

# For cases where the slice stops before the end, or uses a
# non-contiguous range, we do need to create a copy.  However
# this should be transparent to the user.
>>> LinkedList(range(100))[-10::2]
LinkedList([90, 92, 94, 96, 98])

Implementación:

import itertools

class LinkedList(object):
    """Immutable linked list class."""

    def __new__(cls, l=[]):
        if isinstance(l, LinkedList): return l # Immutable, so no copy needed.
        i = iter(l)
        try:
            head = i.next()
        except StopIteration:
            return cls.EmptyList   # Return empty list singleton.

        tail = LinkedList(i)

        obj = super(LinkedList, cls).__new__(cls)
        obj._head = head
        obj._tail = tail
        return obj

    @classmethod
    def cons(cls, head, tail):
        ll =  cls([head])
        if not isinstance(tail, cls):
            tail = cls(tail)
        ll._tail = tail
        return ll

    # head and tail are not modifiable
    @property  
    def head(self): return self._head

    @property
    def tail(self): return self._tail

    def __nonzero__(self): return True

    def __len__(self):
        return sum(1 for _ in self)

    def __add__(self, other):
        other = LinkedList(other)

        if not self: return other   # () + l = l
        start=l = LinkedList(iter(self))  # Create copy, as we'll mutate

        while l:
            if not l._tail: # Last element?
                l._tail = other
                break
            l = l._tail
        return start

    def __radd__(self, other):
        return LinkedList(other) + self

    def __iter__(self):
        x=self
        while x:
            yield x.head
            x=x.tail

    def __getitem__(self, idx):
        """Get item at specified index"""
        if isinstance(idx, slice):
            # Special case: Avoid constructing a new list, or performing O(n) length 
            # calculation for slices like l[3:].  Since we're immutable, just return
            # the appropriate node. This becomes O(start) rather than O(n).
            # We can't do this for  more complicated slices however (eg [l:4]
            start = idx.start or 0
            if (start >= 0) and (idx.stop is None) and (idx.step is None or idx.step == 1):
                no_copy_needed=True
            else:
                length = len(self)  # Need to calc length.
                start, stop, step = idx.indices(length)
                no_copy_needed = (stop == length) and (step == 1)

            if no_copy_needed:
                l = self
                for i in range(start): 
                    if not l: break # End of list.
                    l=l.tail
                return l
            else:
                # We need to construct a new list.
                if step < 1:  # Need to instantiate list to deal with -ve step
                    return LinkedList(list(self)[start:stop:step])
                else:
                    return LinkedList(itertools.islice(iter(self), start, stop, step))
        else:       
            # Non-slice index.
            if idx < 0: idx = len(self)+idx
            if not self: raise IndexError("list index out of range")
            if idx == 0: return self.head
            return self.tail[idx-1]

    def __mul__(self, n):
        if n <= 0: return Nil
        l=self
        for i in range(n-1): l += self
        return l
    def __rmul__(self, n): return self * n

    # Ideally we should compute the has ourselves rather than construct
    # a temporary tuple as below.  I haven't impemented this here
    def __hash__(self): return hash(tuple(self))

    def __eq__(self, other): return self._cmp(other) == 0
    def __ne__(self, other): return not self == other
    def __lt__(self, other): return self._cmp(other) < 0
    def __gt__(self, other): return self._cmp(other) > 0
    def __le__(self, other): return self._cmp(other) <= 0
    def __ge__(self, other): return self._cmp(other) >= 0

    def _cmp(self, other):
        """Acts as cmp(): -1 for self<other, 0 for equal, 1 for greater"""
        if not isinstance(other, LinkedList):
            return cmp(LinkedList,type(other))  # Arbitrary ordering.

        A, B = iter(self), iter(other)
        for a,b in itertools.izip(A,B):
           if a<b: return -1
           elif a > b: return 1

        try:
            A.next()
            return 1  # a has more items.
        except StopIteration: pass

        try:
            B.next()
            return -1  # b has more items.
        except StopIteration: pass

        return 0  # Lists are equal

    def __repr__(self):
        return "LinkedList([%s])" % ', '.join(map(repr,self))

class EmptyList(LinkedList):
    """A singleton representing an empty list."""
    def __new__(cls):
        return object.__new__(cls)

    def __iter__(self): return iter([])
    def __nonzero__(self): return False

    @property
    def head(self): raise IndexError("End of list")

    @property
    def tail(self): raise IndexError("End of list")

# Create EmptyList singleton
LinkedList.EmptyList = EmptyList()
del EmptyList

llist — Tipos de datos de listas enlazadas para Python

El módulo llist implementa estructuras de datos de listas enlazadas. Admite una lista doblemente enlazada, es decir dllist y una estructura de datos de enlace simple sllist.

objetos de lista dll

Este objeto representa una estructura de datos de lista doblemente enlazada.

first

Primero dllistnode objeto en la lista. None si la lista está vacía.

last

Ultimo dllistnode objeto en la lista. Ninguno si la lista está vacía.

Los objetos dllist también admiten los siguientes métodos:

append(x)

Agregar x al lado derecho de la lista y volver insertado dllistnode.

appendleft(x)

Agregar x al lado izquierdo de la lista y volver insertado dllistnode.

appendright(x)

Agregar x al lado derecho de la lista y volver insertado dllistnode.

clear()

Elimina todos los nodos de la lista.

extend(iterable)

Agregar elementos de iterable al lado derecho de la lista.

extendleft(iterable)

Agregar elementos de iterable al lado izquierdo de la lista.

extendright(iterable)

Agregar elementos de iterable al lado derecho de la lista.

insert(x[, before])

Agregar x al lado derecho de la lista si before no se especifica, o insertar x al lado izquierdo de dllistnode before. Devolución insertada dllistnode.

nodeat(index)

Nodo de retorno (de tipo dllistnode) a index.

pop()

Elimina y devuelve el valor de un elemento del lado derecho de la lista.

popleft()

Elimina y devuelve el valor de un elemento del lado izquierdo de la lista.

popright()

Eliminar y devolver el valor de un elemento del lado derecho de la lista

remove(node)

Eliminar node de la lista y devolver el elemento que se almacenó en él.

dllistnode objetos

clase llist.dllistnode([value])

Devuelve un nuevo nodo de lista doblemente enlazado, inicializado (opcionalmente) con value.

dllistnode Los objetos proporcionan los siguientes atributos:

next

Siguiente nodo en la lista. Este atributo es de solo lectura.

prev

Nodo anterior en la lista. Este atributo es de solo lectura.

value

Valor almacenado en este nodo.
Compilado de esta referencia

lista

clase llist.sllist([iterable])
Devuelve una nueva lista enlazada individualmente inicializada con elementos de iterable. Si no se especifica iterable, el nuevo sllist esta vacio.

Se define un conjunto similar de atributos y operaciones para este sllist objeto. Consulte esta referencia para obtener más información.

  • ¿Hay una fuente para esto? ¿Funciona para python3?

    – iggy12345

    16 mayo 2019 a las 20:32

¿Ha sido útil esta solución?