Este es el tercer artículo de la serie de artículos sobre la implementación de listas vinculadas con Python. En la Parte 1 y la Parte 2 de la serie, estudiamos en detalle la lista enlazada única. En este artículo, comenzaremos nuestra discusión sobre la lista de enlaces dobles, que en realidad es una extensión de la lista de enlaces únicos.
En una lista vinculada única, cada node de la lista tiene dos componentes, el valor real del node y la referencia al siguiente node de la lista vinculada. En la lista doblemente enlazada, cada node tiene tres componentes: el valor del node, la referencia al node anterior y la referencia al siguiente node. Para el node de inicio de la lista doblemente enlazada, la referencia al node anterior es nula. De manera similar, para el último node de la lista doblemente enlazada, la referencia al siguiente node es nula.
Pros y contras de una lista doblemente vinculada
Contenido
- 1 Pros y contras de una lista doblemente vinculada
- 2 Implementando la lista doblemente vinculada con Python
- 2.1 Insertar elementos en una lista doblemente vinculada
- 2.2 Insertar elementos en una lista vacía
- 2.3 Insertar elementos al principio
- 2.4 Insertar elementos al final
- 2.5 Insertar artículo después de otro artículo
- 2.6 Insertar artículo antes que otro artículo
- 2.7 Atravesando una lista doblemente enlazada
- 2.8 Eliminar elementos de una lista doblemente vinculada
- 2.9 Invertir una lista doblemente vinculada
- 2.10 Prueba de funciones de lista doblemente enlazadas
- 3 Conclusión
A continuación se muestran algunos de los pros y los contras de una lista doblemente vinculada:
Pros
- A diferencia de una única lista enlazada, la lista doblemente enlazada se puede recorrer y buscar en ambas direcciones. La referencia al siguiente node ayuda a atravesar el node en la dirección de avance, mientras que las referencias a los nodes anteriores permiten el cruce en la dirección de retroceso.
- Las operaciones básicas como la inserción y el borrado son más fáciles de implementar en las listas doblemente enlazadas ya que, a diferencia de las listas enlazadas simples, no necesitamos atravesar el node predecesor y almacenar su referencia. Más bien, en una lista doblemente enlazada, la referencia del node predecesor se puede recuperar del node que queremos eliminar.
Contras
- Uno de los principales inconvenientes de la lista doblemente enlazada es que necesita más espacio de memoria para almacenar una referencia adicional para cada node.
- Es necesario realizar algunos pasos adicionales para realizar operaciones de inserción y eliminación.
Implementando la lista doblemente vinculada con Python
En esta sección, veremos cómo podemos crear una lista doblemente enlazada muy simple en Python. Si ha leído la Parte 1 y la Parte 2 de esta serie de artículos, el código debería ser bastante sencillo.
Como siempre, primero creemos una clase para el único node de la lista. Agrega el siguiente código a tu archivo:
class Node:
def __init__(self, data):
self.item = data
self.nref = None
self.pref = None
Se puede ver en el código anterior, creamos una Node
clase con tres variables miembro: item
, nref
, y pref
. La item
variable almacenará los datos reales del node. El nref
almacena la referencia al siguiente node, mientras que pref
almacena la referencia al node anterior en la lista doblemente enlazada.
A continuación, necesitamos crear la DoublyLinkedList
clase, que contiene diferentes funciones relacionadas con listas doblemente enlazadas. Agrega el siguiente código:
class DoublyLinkedList:
def __init__(self):
self.start_node = None
A lo largo de este artículo, seguiremos agregando funciones a esta clase.
Insertar elementos en una lista doblemente vinculada
En esta sección, veremos las diferentes formas de insertar elementos en una lista doblemente enlazada.
Insertar elementos en una lista vacía
La forma más sencilla de insertar un elemento en una lista doblemente enlazada es insertar un elemento en la lista vacía. La siguiente secuencia de comandos inserta un elemento al comienzo de la lista doblemente enlazada:
def insert_in_emptylist(self, data):
if self.start_node is None:
new_node = Node(data)
self.start_node = new_node
else:
print("list is not empty")
En el script anterior, definimos un método insert_in_emptylist()
. El método primero verifica si la self.start_node
variable lo es None
o no. Si la variable lo es None
, significa que la lista está realmente vacía. A continuación, se crea un nuevo node y su valor se inicializa mediante el valor pasado como parámetro al data
parámetro de la insert_in_emptylist()
función. Finalmente, el valor de la self.start_node
variable se establece en el nuevo node. En caso de que la lista no esté vacía, simplemente se muestra un mensaje al usuario de que la lista no está vacía.
Agregue el insert_in_emptylist()
método a la DoublyLinkedList
clase que creó anteriormente.
Insertar elementos al principio
Para insertar un elemento al principio de la lista doblemente enlazada, primero tenemos que comprobar si la lista está vacía o no. Si la lista está vacía, simplemente podemos usar la lógica definida en el insert_in_emptylist()
para insertar el elemento ya que en una lista vacía, el primer elemento siempre está al principio.
De lo contrario, si la lista no está vacía, debemos realizar tres operaciones:
- Para el nuevo node, la referencia al siguiente node se establecerá en
self.start_node
. - Para el,
self.start_node
la referencia al node anterior se establecerá en el node recién insertado. - Finalmente,
self.start_node
se convertirá en el node recién insertado.
La siguiente secuencia de comandos inserta un elemento al comienzo de la lista doblemente enlazada:
def insert_at_start(self, data):
if self.start_node is None:
new_node = Node(data)
self.start_node = new_node
print("node inserted")
return
new_node = Node(data)
new_node.nref = self.start_node
self.start_node.pref = new_node
self.start_node = new_node
Agregue el insert_at_start()
método a la DoublyLinkedList
clase que creó anteriormente.
Insertar elementos al final
Insertar un elemento al final de la lista doblemente enlazada es algo similar a insertar un elemento al principio. Al principio, debemos verificar si la lista está vacía. Si la lista está vacía, simplemente podemos usar el insert_in_emptylist()
método para insertar el elemento. Si la lista ya contiene algún elemento, recorremos la lista hasta que la referencia al siguiente node se convierte en None
. Cuando la siguiente referencia de node se convierte None
en, significa que el node actual es el último node.
La referencia anterior para el nuevo node se establece en el último node y la siguiente referencia para el último node se establece en el node recién insertado. El script para insertar un elemento en el último node es el siguiente:
def insert_at_end(self, data):
if self.start_node is None:
new_node = Node(data)
self.start_node = new_node
return
n = self.start_node
while n.nref is not None:
n = n.nref
new_node = Node(data)
n.nref = new_node
new_node.pref = n
Agregue el insert_at_end()
método a la DoublyLinkedList
clase que creó anteriormente.
Insertar artículo después de otro artículo
Para insertar un elemento después de otro elemento, primero verificamos si la lista está vacía o no. Si la lista está realmente vacía, simplemente mostramos el mensaje de que la «lista está vacía».
De lo contrario, iteramos a través de todos los nodes en la lista doblemente vinculada. En caso de que no se encuentre el node después del cual queremos insertar el nuevo node, mostramos el mensaje al usuario de que el elemento no se encuentra. De lo contrario, si se encuentra el node, se selecciona y realizamos cuatro operaciones:
- Establezca la referencia anterior del node recién insertado en el node seleccionado.
- Establezca la siguiente referencia del node recién insertado en la siguiente referencia del seleccionado.
- Si el node seleccionado no es el último node, establezca la referencia anterior del siguiente node después del node seleccionado en el node recién agregado.
- Finalmente, establezca la siguiente referencia del node seleccionado al node recién insertado.
El script para insertar un elemento después de otro es el siguiente:
Te puede interesar:Pilas y colas en Python def insert_after_item(self, x, data):
if self.start_node is None:
print("List is empty")
return
else:
n = self.start_node
while n is not None:
if n.item == x:
break
n = n.nref
if n is None:
print("item not in the list")
else:
new_node = Node(data)
new_node.pref = n
new_node.nref = n.nref
if n.nref is not None:
n.nref.prev = new_node
n.nref = new_node
Agregue el insert_after_item()
método a la DoublyLinkedList
clase que creó anteriormente.
Insertar artículo antes que otro artículo
Para insertar un elemento antes que otro elemento, primero verificamos si la lista está vacía o no. Si la lista está realmente vacía, simplemente mostramos el mensaje de que la «lista está vacía».
De lo contrario, iteramos a través de todos los nodes en la lista doblemente vinculada. En caso de que no se encuentre el node antes del cual queremos insertar el nuevo node, mostramos el mensaje al usuario de que no se encuentra el elemento. De lo contrario, si se encuentra el node, se selecciona y realizamos cuatro operaciones:
- Establezca la siguiente referencia del node recién insertado en el node seleccionado.
- Establece la referencia anterior del node recién insertado a la referencia anterior del seleccionado.
- Establezca la siguiente referencia del node anterior al node seleccionado, al node recién agregado.
- Finalmente, establezca la referencia anterior del node seleccionado al node recién insertado.
El script para agregar un elemento antes de otro elemento en una lista doblemente enlazada es el siguiente:
def insert_before_item(self, x, data):
if self.start_node is None:
print("List is empty")
return
else:
n = self.start_node
while n is not None:
if n.item == x:
break
n = n.nref
if n is None:
print("item not in the list")
else:
new_node = Node(data)
new_node.nref = n
new_node.pref = n.pref
if n.pref is not None:
n.pref.nref = new_node
n.pref = new_node
Agregue el insert_before_item()
método a la DoublyLinkedList
clase que creó anteriormente.
Atravesando una lista doblemente enlazada
Recorrer una lista doblemente vinculada es muy similar a recorrer una única lista vinculada. El guión es el siguiente:
def traverse_list(self):
if self.start_node is None:
print("List has no element")
return
else:
n = self.start_node
while n is not None:
print(n.item , " ")
n = n.nref
Agregue el traverse_list()
método a la DoublyLinkedList
clase que creó anteriormente.
Eliminar elementos de una lista doblemente vinculada
Al igual que la inserción, puede haber varias formas de eliminar elementos de una lista doblemente enlazada. En esta sección, repasaremos algunos de ellos.
Eliminar elementos desde el principio
La forma más sencilla de eliminar un elemento de una lista doblemente enlazada es desde el principio. Para hacerlo, todo lo que tiene que hacer es establecer el valor del node de inicio en el siguiente node y luego establecer la referencia anterior del node de inicio en None
. Sin embargo, antes de hacerlo, debemos realizar dos comprobaciones. Primero, necesitamos ver si la lista está vacía. Y luego tenemos que ver si la lista contiene solo un elemento o no. Si la lista contiene solo un elemento, simplemente podemos establecer el node de inicio en None
. El siguiente script se puede utilizar para eliminar elementos del inicio de la lista doblemente enlazada.
def delete_at_start(self):
if self.start_node is None:
print("The list has no element to delete")
return
if self.start_node.nref is None:
self.start_node = None
return
self.start_node = self.start_node.nref
self.start_prev = None;
Agregue el delete_at_start()
método a la DoublyLinkedList
clase que creó anteriormente.
Eliminar elementos del final
Para eliminar el elemento del final, nuevamente verificamos si la lista está vacía o si la lista contiene un solo elemento. Si la lista contiene un solo elemento, todo lo que tenemos que hacer es establecer el node de inicio en None
. Si la lista tiene más de un elemento, iteramos a través de la lista hasta que se alcanza el último node. Una vez que llegamos al último node, establecemos la siguiente referencia del node anterior al último node, al None
que en realidad elimina el último node. El siguiente script se puede utilizar para eliminar el elemento del final.
def delete_at_end(self):
if self.start_node is None:
print("The list has no element to delete")
return
if self.start_node.nref is None:
self.start_node = None
return
n = self.start_node
while n.nref is not None:
n = n.nref
n.pref.nref = None
Agregue el delete_at_end()
método a la DoublyLinkedList
clase que creó anteriormente.
Eliminar elementos por valor
Eliminar un elemento por valor es la más complicada de todas las funciones de eliminación en listas doblemente vinculadas, ya que se deben manejar varios casos para eliminar un elemento por valor. Primero veamos cómo se ve la función y luego veremos la explicación de la pieza de código individual.
def delete_element_by_value(self, x):
if self.start_node is None:
print("The list has no element to delete")
return
if self.start_node.nref is None:
if self.start_node.item == x:
self.start_node = None
else:
print("Item not found")
return
if self.start_node.item == x:
self.start_node = self.start_node.nref
self.start_node.pref = None
return
n = self.start_node
while n.nref is not None:
if n.item == x:
break;
n = n.nref
if n.nref is not None:
n.pref.nref = n.nref
n.nref.pref = n.pref
else:
if n.item == x:
n.pref.nref = None
else:
print("Element not found")
En el script anterior creamos una delete_element_by_value()
función que toma el valor del node como parámetro y borra ese node. Al comienzo de la función, verificamos si la lista está vacía o no. Si la lista está vacía, simplemente le mostramos al usuario que la lista está vacía.
Esta lógica se implementa en el siguiente código:
if self.start_node is None:
print("The list has no element to delete")
return
A continuación, verificamos si la lista tiene un solo elemento y ese elemento es realmente el elemento que queremos eliminar. Si el único elemento es el que queremos eliminar, simplemente establecemos self.start_node
en, lo None
que significa que la lista ahora no tendrá ningún elemento. Si solo hay un elemento y ese no es el elemento que queremos eliminar, simplemente mostraremos el mensaje de que no se encuentra el elemento a eliminar.
El siguiente código implementa esta lógica:
if self.start_node.nref is None:
if self.start_node.item == x:
self.start_node = None
else:
print("Item not found")
return
A continuación, manejamos el caso en el que la lista tiene más de un elemento, pero el elemento a eliminar es el primer elemento. En ese caso, simplemente ejecutamos la lógica que escribimos para el método delete_at_start()
. El siguiente fragmento de código elimina un elemento desde el principio en el caso de varios elementos:
if self.start_node.item == x:
self.start_node = self.start_node.nref
self.start_node.pref = None
return
Finalmente, si la lista contiene varios elementos y el elemento a eliminar no es el primer elemento, recorremos todos los elementos de la lista excepto el último y vemos si alguno de los nodes tiene el valor que coincide con el valor que se eliminará. Si se encuentra el node, realizamos las siguientes dos operaciones:
- Establezca el valor de la siguiente referencia del node anterior a la siguiente referencia del node que se eliminará.
- Establezca el valor anterior del siguiente node a la referencia anterior del node que se eliminará.
Finalmente, si el node a eliminar es el último node, se establece en la siguiente referencia del node anterior al último node None
. El siguiente script implementa esta lógica:
n = self.start_node
while n.nref is not None:
if n.item == x:
break;
n = n.nref
if n.nref is not None:
n.pref.nref = n.nref
n.nref.pref = n.pref
else:
if n.item == x:
n.pref.nref = None
else:
print("Element not found")
Agregue el delete_element_by_value()
método a la DoublyLinkedList
clase que creó anteriormente.
Invertir una lista doblemente vinculada
Para revertir una lista doblemente enlazada, básicamente tienes que realizar las siguientes operaciones:
Te puede interesar:Introducción al módulo de colecciones de Python- La siguiente referencia del node de inicio debe establecerse como ninguna porque el primer node se convertirá en el último node de la lista invertida.
- La referencia anterior del último node debe establecerse en
None
ya que el último node se convertirá en el node anterior. - Las siguientes referencias de los nodes (excepto el primer y último node) en la lista original deben intercambiarse con las referencias anteriores.
El guión para invertir una lista doblemente enlazada es el siguiente:
def reverse_linked_list(self):
if self.start_node is None:
print("The list has no element to delete")
return
p = self.start_node
q = p.nref
p.nref = None
p.pref = q
while q is not None:
q.pref = q.nref
q.nref = p
p = q
q = q.pref
self.start_node = p
Agregue el reverse_linked_list()
método a la DoublyLinkedList
clase que creó anteriormente.
Prueba de funciones de lista doblemente enlazadas
En esta sección, probaremos las funciones doblemente vinculadas que creamos en las secciones anteriores.
Primero creemos el objeto de la DoublyLinkedList
clase. Ejecute el siguiente script:
new_linked_list = DoublyLinkedList()
Prueba de funciones de inserción
Probemos primero las funciones de inserción. Primero agregaremos elementos en la lista vacía. Ejecute el siguiente script:
new_linked_list.insert_in_emptylist(50)
Ahora, si recorre la lista, debería ver 50 como el único elemento en la lista como se muestra a continuación:
new_linked_list.traverse_list()
Salida:
50
Ahora agreguemos algunos elementos al principio. Ejecute el siguiente script:
new_linked_list.insert_at_start(10)
new_linked_list.insert_at_start(5)
new_linked_list.insert_at_start(18)
Ahora, si recorre la lista, debería ver los siguientes elementos en la lista:
18
5
10
50
Para agregar los elementos al final, ejecute el siguiente script:
new_linked_list.insert_at_end(29)
new_linked_list.insert_at_end(39)
new_linked_list.insert_at_end(49)
Ahora, si recorre la lista doblemente vinculada, debería ver los siguientes elementos:
Te puede interesar:Administradores de contexto de Python18
5
10
50
29
39
49
Insertemos un elemento después de 50.
new_linked_list.insert_after_item(50, 65)
Ahora la lista debería verse así:
18
5
10
50
65
29
39
49
Finalmente, agreguemos un elemento antes del artículo 29.
new_linked_list.insert_before_item(29, 100)
La lista en este momento debe contener los siguientes elementos:
18
5
10
50
65
100
29
39
49
Prueba de funciones de eliminación
Probemos ahora las funciones de eliminación en los elementos que insertamos en las últimas secciones. Primero eliminemos un elemento desde el principio.
new_linked_list.delete_at_start()
El elemento 18 se eliminará y la lista se verá así:
5
10
50
65
100
29
39
49
De manera similar, la siguiente secuencia de comandos elimina el elemento del final de la lista doblemente vinculada:
new_linked_list.delete_at_end()
Si recorre la lista ahora, se devolverán los siguientes elementos:
5
10
50
65
100
29
39
Finalmente, también puede eliminar los elementos por valor usando la delete_element_by_value()
función como se muestra a continuación:
new_linked_list.delete_element_by_value(65)
Si recorre la lista ahora, verá que el elemento 65 se eliminará de la lista.
Prueba de la función inversa
Finalmente, invirtamos la lista usando la reverse_linked_list()
función. Ejecute el siguiente script:
new_linked_list.reverse_linked_list()
Ahora, si recorre la lista, verá la lista enlazada invertida:
39
29
100
50
10
5
Conclusión
La lista doblemente vinculada es extremadamente útil específicamente cuando tiene que realizar muchas inserciones y operaciones de eliminación. Los enlaces a los nodes anterior y siguiente facilitan la inserción y eliminación de nuevos elementos sin realizar un seguimiento de los nodes anteriores y siguientes.
En este artículo, vimos cómo se puede implementar una lista doblemente enlazada con Python. También vimos diferentes formas de realizar operaciones de inserción y eliminación en una lista doblemente vinculada. Finalmente, estudiamos cómo revertir una lista doblemente enlazada.