Sebastian Gomez
Estructuras de datos en Go: Listas Enlazadas Circulares
Vamos a explorar el fascinante mundo de las listas enlazadas circulares en Go. Prepárate para una aventura llena de historia, código, ejemplos y desafíos.
Historia de las listas enlazadas circulares
Las listas enlazadas circulares son una evolución de las listas enlazadas simples y doblemente enlazadas que ya hemos explorado en artículos anteriores. La idea se popularizó hace décadas con la intención de aprovechar al máximo la memoria y los recursos disponibles en los sistemas de aquella época.
En una lista enlazada circular, el último nodo apunta al primer nodo, creando un ciclo. Esto puede ser útil en ciertos casos de uso que veremos más adelante. Ahora, sumerjámonos en el maravilloso mundo de Go.
La implementación más común en Go
package main
import "fmt"
type Node struct {
value int
next *Node
}
type CircularLinkedList struct {
head *Node
tail *Node
size int
}
func (c *CircularLinkedList) Add(value int) {
newNode := &Node{value: value}
if c.head == nil {
c.head = newNode
c.tail = newNode
newNode.next = c.head
} else {
c.tail.next = newNode
newNode.next = c.head
c.tail = newNode
}
c.size++
}
func (c *CircularLinkedList) Display() {
if c.size == 0 {
fmt.Println("La lista está vacía")
return
}
temp := c.head
for i := 0; i < c.size; i++ {
fmt.Printf("%d -> ", temp.value)
temp = temp.next
}
fmt.Println()
}
func main() {
cll := &CircularLinkedList{}
cll.Add(1)
cll.Add(2)
cll.Add(3)
cll.Display()
}Esta implementación utiliza una estructura Node para representar cada nodo de la lista y una estructura CircularLinkedList para gestionar la lista enlazada circular. Implementamos el método Add para agregar elementos y el método Display para mostrar la lista.
Fíjate en un detalle importante: cuando agregamos el primer nodo, este apunta a sí mismo (newNode.next = c.head), y a partir de ahí cada nodo nuevo vuelve a enlazar el final con el inicio. Así mantenemos siempre la invariante de una lista circular: la cola apunta a la cabeza (tail.next == head).
Casos de uso y ejemplos
Las listas enlazadas circulares son especialmente útiles en situaciones en las que necesitamos una estructura de datos circular. Algunos casos de uso incluyen:
- Planificación de procesos: En sistemas operativos, las listas enlazadas circulares se pueden utilizar para implementar algoritmos de planificación de procesos, como el algoritmo Round Robin.
- Reproducción de música: En aplicaciones de música, las listas enlazadas circulares pueden usarse para crear una lista de reproducción en bucle, donde la última canción se conecta con la primera, permitiendo una reproducción continua.
- Simulaciones de eventos cíclicos: En simulaciones donde los eventos ocurren en ciclos, como las fases lunares o las estaciones del año, las listas enlazadas circulares pueden ser una excelente opción para modelar y gestionar estos eventos.
- Gestión de recursos compartidos: Las listas enlazadas circulares pueden utilizarse para asignar y gestionar recursos compartidos entre diferentes usuarios o procesos de forma equitativa y eficiente.
Ahora que conocemos algunos casos de uso, exploremos un ejemplo en Go para que todo quede más claro.
Ejemplo: Rotación de elementos en una lista enlazada circular
func (c *CircularLinkedList) Rotate(k int) {
if c.size == 0 {
return
}
k = k % c.size
if k == 0 {
return
}
// Avanzamos k pasos desde la cabeza para localizar el nodo que
// se convertirá en la nueva cola.
kthNode := c.head
for count := 1; count < k; count++ {
kthNode = kthNode.next
}
// La nueva cabeza es el siguiente al nodo k.
newHead := kthNode.next
// Reconstruimos los enlaces manteniendo la invariante circular:
// la cola actual ya apunta a la cabeza actual, así que solo
// necesitamos mover head y tail y cerrar el ciclo.
c.tail.next = c.head
c.head = newHead
c.tail = kthNode
c.tail.next = c.head
}
func main() {
cll := &CircularLinkedList{}
cll.Add(1)
cll.Add(2)
cll.Add(3)
cll.Add(4)
cll.Add(5)
cll.Display()
cll.Rotate(2)
cll.Display()
}En este ejemplo implementamos el método Rotate para rotar los elementos de la lista enlazada circular. Al ejecutar el programa con la lista 1 -> 2 -> 3 -> 4 -> 5, una rotación de k = 2 nos deja 3 -> 4 -> 5 -> 1 -> 2.
¿Por qué funciona? La clave está en respetar siempre la invariante de la lista circular: la cola debe apuntar a la cabeza. Primero avanzamos k pasos para encontrar el nodo que será la nueva cola (kthNode). Su sucesor (kthNode.next) será la nueva cabeza. Como en una lista circular la cola ya apunta a la cabeza, basta con mover los punteros head y tail y volver a cerrar el ciclo con c.tail.next = c.head. Así ningún nodo queda huérfano y el anillo sigue siendo válido.
Nota: El código de arriba es Go estándar y solo usa el paquete fmt, así que sigue siendo válido en las versiones actuales de Go.go run main.go para confirmar la salida antes de adaptarlo a tu proyecto.
10 ejercicios para practicar
Es hora de poner en práctica tus habilidades. Aquí tienes una lista de 10 ejercicios para que practiques con listas enlazadas circulares en Go.
- Implementar un método para eliminar un nodo de la lista enlazada circular.
- Implementar un método para insertar un nodo en una posición específica de la lista.
- Crear un programa que simule una ruleta de premios utilizando una lista enlazada circular.
- Implementar un método para invertir el orden de los elementos en la lista enlazada circular.
- Crear un programa que simule una aplicación de música utilizando listas enlazadas circulares.
- Implementar un método para dividir una lista enlazada circular en dos listas enlazadas circulares.
- Crear un programa que simule el algoritmo de planificación Round Robin utilizando listas enlazadas circulares.
- Implementar un método para fusionar dos listas enlazadas circulares en una sola lista.
- Crear un programa que simule una carrera de relevos utilizando listas enlazadas circulares.
- Implementar un método para detectar si una lista enlazada circular contiene un ciclo no deseado (por ejemplo, un enlace que apunta a un nodo intermedio en lugar de a la cabeza) y corregirlo. Ten en cuenta que una lista circular bien formada siempre tiene un único ciclo: el de la cola hacia la cabeza.
No olvides compartir tus soluciones
Te invitamos a dejar la solución como un Pull Request a este repositorio: Go Para Principiantes. Me encantaría ver cómo abordas estos desafíos para aprender juntos.
Recuerda que la práctica hace al maestro, así que no dudes en experimentar, cometer errores y aprender de ellos. Sigue adelante, valiente programador.
Conclusiones
Hemos explorado el interesante mundo de las listas enlazadas circulares en Go, aprendiendo sobre su historia, casos de uso y ejemplos. Además, hemos visto cómo implementar y utilizar una lista enlazada circular mediante una estructura Node y una estructura CircularLinkedList, e incluso cómo rotar sus elementos sin romper la invariante circular. Las listas enlazadas circulares son una herramienta poderosa y versátil que se adapta a una amplia gama de aplicaciones.
No olvides poner en práctica tus habilidades resolviendo los 10 ejercicios propuestos y compartiendo tus soluciones con nuestra comunidad. No olvides revisar el blog regularmente para descubrir más aventuras en el mundo de la programación.
Eso es todo, espero que este post te sea de utilidad y lo puedas aplicar a algún proyecto que tengas en mente. Déjame un comentario si te sirvió o si tienes alguna duda, y si te gustó, compártelo usando los links a las redes sociales aquí abajo. Hasta la próxima.
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.