Lista enlazada circular: ventajas y desventajas

โšก Resumen inteligente

Las listas enlazadas circulares organizan los nodos de manera que el รบltimo nodo vuelva al primero, lo que proporciona una estructura continua y sin valores nulos que se adapta a la programaciรณn round-robin, los anillos de tokens y cualquier flujo de trabajo que necesite un recorrido sin interrupciones.

  • ๐Ÿ“š Definiciรณn: Cada nodo contiene un valor y un puntero al siguiente nodo, y el puntero del รบltimo nodo enlaza con el primero, creando un ciclo cerrado.
  • ๐Ÿ“Œ Nuestras Operafunciones: La inserciรณn, la eliminaciรณn y el recorrido giran en torno a la actualizaciรณn de uno o dos punteros siguientes, preservando al mismo tiempo el ciclo.
  • ๐Ÿ› ๏ธ Implementaciรณn en C: Los nodos basados โ€‹โ€‹en estructuras con inserciones respaldadas por malloc y eliminaciones respaldadas por free cubren tanto los casos de posiciรณn actual como los de nodo posterior.
  • โœ… Ventajas: Sin desreferencias NULL, transiciones perfectas de principio a fin y variantes doblemente circulares que reducen a la mitad las bรบsquedas en el peor de los casos.
  • โš ๏ธ Desventajas: Control de bucles mรกs complicado, mayor complejidad que las listas enlazadas simples y bucles infinitos si la terminaciรณn se escribe incorrectamente.
  • ๐ŸŽฏ Aplicaciones: Planificaciรณn de CPU por turnos, redes de anillo de tokens, bรบferes circulares, listas de reproducciรณn multimedia y unidades de visualizaciรณn continua.

Lista enlazada circular

ยฟQuรฉ es una lista enlazada circular?

Una lista enlazada circular es una secuencia de nodos dispuestos de manera que cada nodo pueda ser...traccada โ€œnodoโ€ es un elemento autorreferencial con punteros a uno o dos nodos en su proximidad inmediata.

A continuaciรณn se muestra una representaciรณn de una lista enlazada circular con 3 nodos.

Lista enlazada circular

Aquรญ, puedes ver que cada nodo es retraccapaz de sรญ misma. El ejemplo que se muestra arriba es una lista enlazada simple circular.

Nota: La lista enlazada circular mรกs simple es un รบnico nodo cuyo puntero siguiente tracvuelve a sรญ mismo, como se muestra a continuaciรณn.

Lista enlazada circular

Bรกsico Operaciones en listas enlazadas circulares

Las tres operaciones bรกsicas en una lista enlazada circular son:

  1. Inserciรณn
  2. Eliminaciรณn y
  3. Travesรญa
  • La inserciรณn es el proceso de colocar un nodo en una posiciรณn especรญfica en la lista circular enlazada.
  • La eliminaciรณn es el proceso de eliminar un nodo existente de la lista vinculada. El nodo puede identificarse por la apariciรณn de su valor o por su posiciรณn.
  • El recorrido de una lista enlazada circular es el proceso de mostrar todo el contenido de la lista enlazada y volver a mostrarlo.tracvolviendo al nodo de origen.

La siguiente secciรณn explica cรณmo funciona la inserciรณn y los dos tipos de inserciรณn posibles en una lista circular enlazada simple.

Inserciรณn Operadisrupciรณn

Primero, crea un nodo cuyo puntero next apunte a sรญ mismo, como se muestra a continuaciรณn. Sin este nodo semilla, la primera inserciรณn se convierte en el primer nodo de la lista.

Inserciรณn Operadisrupciรณn

A continuaciรณn, existen dos posibilidades:

  • Inserciรณn en la posiciรณn actual de la lista enlazada circular. Esto equivale a insertar un elemento al principio o al final de una lista enlazada simple regular; en una lista enlazada circular, el principio y el final coinciden.
  • Inserciรณn despuรฉs de un nodo indexado. El nodo debe identificarse mediante un nรบmero de รญndice correspondiente al valor de su elemento.

Para insertar un nodo al principio o al final de la lista enlazada circular, es decir, en la posiciรณn donde se agregรณ el primer nodo, siga los pasos que se indican a continuaciรณn:

  • Tendrรกs que romper el autovรญnculo existente con el nodo existente.
  • El siguiente puntero del nuevo nodo se vincularรก al nodo existente.
  • El siguiente puntero del รบltimo nodo apuntarรก al nodo insertado.

NOTA: El puntero que marca el inicio o el final del cรญrculo se puede reasignar a cualquier nodo. El recorrido seguirรก devolviendo al mismo nodo, como se explica mรกs adelante en este artรญculo.

Los pasos en (a) i-iii se muestran a continuaciรณn:

Inserciรณn Operadisrupciรณn

(Nodo existente)

Inserciรณn Operadisrupciรณn

Paso 1) Romper el enlace existente

Inserciรณn Operadisrupciรณn

Paso 2) Crear un enlace directo (desde un nuevo nodo a un nodo existente)

Inserciรณn Operadisrupciรณn

Paso 3) Crear un enlace de bucle al primer nodo.

A continuaciรณn, intentarรก la inserciรณn despuรฉs de un nodo.

Por ejemplo, inserte โ€œVALUE2โ€ despuรฉs del nodo que contiene โ€œVALUE0โ€, suponiendo que el punto de partida es el nodo con โ€œVALUE0โ€.

  • Rompe el vรญnculo entre el primer y el segundo nodo, y coloca el nodo con โ€œVALUE2โ€ en medio.
  • El puntero siguiente del primer nodo apunta al nuevo nodo, y el puntero siguiente del nuevo nodo apunta a lo que antes era el segundo nodo.
  • El resto de la disposiciรณn permanece sin cambios. Todos los nodos son retraccapaces de hacerlo por sรญ mismos.

NOTA: Debido a que la disposiciรณn es cรญclica, el procedimiento para insertar un nodo es idรฉntico independientemente de la posiciรณn que se elija. El puntero que cierra el ciclo se comporta como cualquier otro puntero de la lista.

Esto se muestra a continuaciรณn:

Inserciรณn Operadisrupciรณn

(Digamos que sรณlo hay dos nodos. Este es un caso trivial)

Inserciรณn Operadisrupciรณn

Paso 1) Retire el enlace interno entre los nodos conectados.

Inserciรณn Operadisrupciรณn

Paso 2) Conecte el nodo del lado izquierdo al nuevo nodo

Inserciรณn Operadisrupciรณn

Paso 3) Conecte el nuevo nodo al nodo del lado derecho.

Eliminacion Operadisrupciรณn

Supongamos una lista enlazada circular de 3 nodos. Los dos casos de eliminaciรณn son:

  • Eliminar el elemento actual
  • Eliminaciรณn despuรฉs de un elemento.

Eliminaciรณn al principio/final:

  1. Atraviese hasta el primer nodo desde el รบltimo nodo.
  2. Eliminar elementos desde el final requiere solo un paso de recorrido, desde el รบltimo nodo hasta el primero.
  3. Elimine el enlace entre el รบltimo nodo y el primer nodo.
  4. Vincula el รบltimo nodo al siguiente elemento del primer nodo.
  5. Libera el primer nodo.

Eliminacion Operadisrupciรณn

(Configuraciรณn existente)

Eliminacion Operadisrupciรณn

Paso 1) Eliminar el enlace circular

Eliminacion Operadisrupciรณn

Paso 2) Eliminar el vรญnculo entre el primero y el siguiente, vincular el รบltimo nodo al nodo siguiente al primero.

Eliminacion Operadisrupciรณn

Paso 3) Liberar/desasignar el primer nodo

Eliminaciรณn despuรฉs de un nodo:

  1. Recorre la lista hasta que el siguiente nodo sea el que se va a eliminar.
  2. Vaya al siguiente nodo y coloque un puntero en el nodo anterior.
  3. Conecte el nodo anterior al nodo posterior al nodo actual, utilizando su puntero siguiente.
  4. Libere el nodo actual (desvinculado).

Eliminacion Operadisrupciรณn

Paso 1) Digamos que necesitamos eliminar un nodo con "VALOR1".

Eliminacion Operadisrupciรณn

Paso 2) Elimine el vรญnculo entre el nodo anterior y el nodo actual, luego vincule el nodo anterior directamente al nodo al que apunta el puntero siguiente del nodo actual (el nodo despuรฉs de VALUE1).

Eliminacion Operadisrupciรณn

Paso 3) Liberar o desasignar el nodo actual.

Recorrido de una lista enlazada circular

Para recorrer una lista enlazada circular desde el รบltimo puntero, primero compruebe si este es nulo. Si no lo es, verifique si la lista tiene un solo elemento. De lo contrario, recorra la lista con un puntero temporal hasta llegar nuevamente al รบltimo puntero, como se muestra en la animaciรณn a continuaciรณn.

Recorrido de una lista enlazada circular

Ventajas de la lista enlazada circular

Algunas de las ventajas de las listas enlazadas circulares son:

  1. No se requiere una asignaciรณn NULL en el cรณdigo. La lista circular nunca apunta a un puntero NULL a menos que se desasigne por completo.
  2. Las listas enlazadas circulares son ventajosas para las operaciones de final de lista porque el principio y el final coinciden. Algorithms Los mรฉtodos de planificaciรณn como round-robin pueden recorrer los procesos en cola de forma limpia, sin encontrar punteros colgantes o nulos.
  3. Una lista enlazada circular aรบn admite todas las operaciones regulares de una lista enlazada simple. lista doblemente enlazada Incluso puede eliminar la necesidad de recorrer toda la lista para localizar un elemento; en el peor de los casos, el objetivo se encuentra frente al puntero de inicio, por lo que como mรกximo es necesario recorrer la mitad de la lista.

Desventajas de la lista enlazada circular

Las desventajas de utilizar una lista enlazada circular se encuentran a continuaciรณn:

  1. Las listas circulares son mรกs complejas que listas enlazadas individualmente.
  2. RevInvertir una lista circular es mรกs complejo que invertir una lista enlazada simple o doble.
  3. Si la terminaciรณn del bucle no se maneja con cuidado, el cรณdigo de recorrido puede entrar en un bucle infinito.
  4. Es mรกs difรญcil encontrar el final de la lista y escribir las condiciones de control de bucle correctas.
  5. Insertar al principio requiere recorrer toda la lista para llegar al รบltimo nodo (desde una perspectiva de implementaciรณn).

Lista enlazada individualmente como lista enlazada circular

Le recomendamos leer e implementar el cรณdigo C que aparece a continuaciรณn. Este cรณdigo ilustra la aritmรฉtica de punteros asociada a una lista enlazada simple circular.

#include<stdio.h>
#include<stdlib.h>

struct node
{
    int item;
    struct node *next;
};

struct node* addToEmpty(struct node*,int);
struct node *insertCurrent(struct node *, int);
struct node *insertAfter(struct node *, int, int);
struct node *removeAfter(struct node *, int);
struct node *removeCurrent(struct node *);

void peek(struct node *);

int main()
{
...

Lista individualmente vinculada

Explicaciรณn del cรณdigo:

  1. Las dos primeras lรญneas de cรณdigo son los archivos de encabezado necesarios incluidos.
  2. La siguiente secciรณn define la estructura de cada nodo autorreferencial. Contiene un valor y un puntero del mismo tipo que la estructura.
  3. Cada instancia de estructura se vincula con otros objetos de estructura del mismo tipo.
  4. Existen diferentes prototipos de funciones para:
    1. Agregar un elemento a una lista enlazada vacรญa
    2. Insertando en el actualmente apuntado Posiciรณn de una lista circular enlazada.
    3. Insertar despuรฉs de un particular indexado valor en la lista vinculada.
    4. Quitar/eliminar despuรฉs de un particular indexado valor en la lista vinculada.
    5. Eliminar en la posiciรณn actual de una lista enlazada circular
  5. La รบltima funciรณn imprime cada elemento a travรฉs de un recorrido circular en cualquier estado de la lista vinculada.
int main()
{
    struct node *last = NULL;
    last = insertCurrent(last,4);
    last = removeAfter(last, 4);
    peek(last);
    return 0;
}

struct node* addToEmpty(struct node*last, int data)
{
    struct node *temp = (struct node *)malloc(sizeof( struct node));
    temp->item = data;
    last = temp;
    last->next = last;
    return last;
}
  
struct node *insertCurrent(struct node *last, int data)

Lista individualmente vinculada

Explicaciรณn del cรณdigo:

  1. Para el cรณdigo addToEmpty, asigne un nodo vacรญo utilizando la funciรณn malloc().
  2. Coloque los datos entrantes en el nodo temporal.
  3. Asigne el nodo temporal al รบltimo nodo y establezca su puntero siguiente hacia sรญ mismo, de modo que el nodo รบnico apunte de nuevo hacia sรญ mismo.
  4. Devuelve el รบltimo puntero al contexto principal de la aplicaciรณn.
struct node *insertCurrent(struct node *last, int data)
{
    if(last == NULL)
    {
       return    addToEmpty(last, data);
    }
    struct node *temp = (struct node *)malloc(sizeof( struct node));
    temp -> item = data;
    temp->next = last->next;
    last->next = temp;
    return last;
}
struct node *insertAfter(struct node *last, int data, int item)
{
    struct node *temp = last->next, *prev = temp, *newnode =NULL;
&#8230;

Lista individualmente vinculada

Explicaciรณn del cรณdigo

  1. Si la lista estรก vacรญa, pase el control a addToEmpty() y devuelva el control.
  2. Crea un nodo temporal para colocarlo despuรฉs del nodo actual.
  3. Conecte los punteros como se muestra en el diagrama anterior.
  4. Devuelve el รบltimo puntero, que coincide con el patrรณn utilizado en la funciรณn anterior.
...
struct node *insertAfter(struct node *last, int data, int item)
{
    struct node *temp = last->next, *prev = temp, *newnode =NULL;
    if (last == NULL)
    {
       return addToEmpty(last, item);
    }
    do
    {
        prev = temp;
        temp = temp->next;
    } while (temp->next != last && temp->item != data );

    if(temp->item != data)
    {
       printf("Element not found. Please try again");
...

Lista individualmente vinculada

Explicaciรณn del cรณdigo:

  1. Si la lista estรก vacรญa, ignore la clave de bรบsqueda, agregue el elemento actual como el รบnico nodo en la lista y devuelva el control.
  2. En cada iteraciรณn del bucle do-while, un puntero anterior almacena el รบltimo resultado recorrido.
  3. Solo entonces se produce el siguiente paso del recorrido.
  4. El bucle do-while finaliza cuando se encuentran los datos de destino o cuando temp vuelve a alcanzar el รบltimo puntero. El siguiente bloque de cรณdigo decide quรฉ hacer con el elemento encontrado.
...
    if(temp->item != data)
    {
       printf("Element not found. Please try again");
       return last;
    }
    else
    {
   	 newnode = (struct node *)malloc(sizeof(struct node));
             newnode->item = item;
             prev->next = newnode;
             newnode->next = temp;
    }
    return last;
}

struct node *removeCurrent(struct node *last)
...

Lista individualmente vinculada

Explicaciรณn del cรณdigo:

  1. Si se ha recorrido toda la lista pero no se encuentra el elemento, muestre un mensaje de "Elemento no encontrado" y devuelva el control a quien realizรณ la llamada.
  2. Si se encuentra el nodo de destino, asigne un nuevo nodo para insertar el valor.
  3. Enlace el nodo anterior al nuevo nodo, y vincular el puntero siguiente del nuevo nodo a temp (la variable de recorrido).
  4. Esto coloca el nuevo elemento inmediatamente despuรฉs del nodo de destino en la lista enlazada circular. A continuaciรณn, el control regresa a quien realizรณ la llamada.
struct node *removeCurrent(struct node *last)
{
    if(last == NULL)
    {
        printf("Element Not Found");
        return NULL;
    }
    struct node *temp = last->next;
    last->next = temp->next;
    free(temp);
    return last;
}

struct node *removeAfter(struct node *last, int data)

Lista individualmente vinculada

Explicaciรณn del cรณdigo

  1. Para eliminar el รบltimo nodo (el actual), primero compruebe si la lista estรก vacรญa. Si lo estรก, no se puede eliminar ningรบn elemento.
  2. La variable temporal avanza un enlace.
  3. Vincula el รบltimo puntero al nodo siguiente al primer nodo.
  4. Libere el puntero temporal para desasignar el nodo no vinculado.
struct node *removeAfter(struct node *last,int data)
{
    struct node *temp = NULL,*prev = NULL;
    if (last == NULL)
    {
   	 printf("Linked list empty. Cannot remove any element\n");
   	 return NULL;
    }
    temp = last->next;
    prev = temp;
    do
    {
        prev = temp;
        temp = temp->next;
    } while (temp->next != last && temp->item != data );

    if(temp->item != data)
    {
      printf("Element not found");
...

Lista individualmente vinculada

Explicaciรณn del cรณdigo

  1. Al igual que con la funciรณn de eliminaciรณn anterior, primero compruebe si la lista estรก vacรญa. Si lo estรก, no se puede eliminar ningรบn elemento.
  2. Dos punteros Se asignan posiciones especรญficas para localizar el elemento a eliminar.
  3. Los punteros avanzan uno tras otro (temperatura de los senderos anteriores).
  4. El recorrido continรบa hasta que se encuentra el elemento de destino o el siguiente puntero vuelve a alcanzar el รบltimo nodo.
    if(temp->item != data)
    {
        printf("Element not found");
        return last;
    }
    else
    {
        prev->next = temp->next;
        free(temp);
    }
    return last;
}

void peek(struct node * last)
{
    struct node *temp = last;
    if (last == NULL)
    {
   return;

Lista individualmente vinculada

Explicaciรณn del programa

  1. Si se recorre toda la lista enlazada sin encontrar el elemento deseado, se muestra un mensaje de "Elemento no encontrado".
  2. De lo contrario, el elemento se desvincula y se libera en los pasos 3 y 4.
  3. El puntero anterior estรก vinculado al nodo al que apunta el siguiente puntero de temp (el nodo que sigue al que se estรก eliminando).
  4. A continuaciรณn, se libera el puntero temporal.
...
void peek(struct node * last)
{
    struct node *temp = last;
    if (last == NULL)
    {
         return;  
    }
    if(last -> next == last)
    {
        printf("%d-", temp->item);
    }
    while (temp != last)
    {
       printf("%d-", temp->item);
       temp = temp->next;
    }
}

Lista individualmente vinculada

Explicaciรณn del cรณdigo

  1. El recorrido de exploraciรณn no es posible si no hay nodos; el usuario primero debe asignar o insertar un nodo.
  2. Si solo hay un nodo, no es necesario recorrerlo: el contenido del nodo se imprime directamente y el bucle while no se ejecuta.
  3. Si hay mรกs de un nodo, la funciรณn temp imprime todos los elementos hasta el รบltimo.
  4. En el momento en que se alcanza el รบltimo elemento, el bucle termina y la funciรณn devuelve el control a main().

Aplicaciones de la Lista Enlazada Circular

  • Implementaciรณn de programaciรณn por turnos en procesos del sistema y programaciรณn circular en grรกficos de alta velocidad.
  • Planificaciรณn mediante anillo de tokens en redes informรกticas.
  • Se utiliza en unidades de visualizaciรณn, como por ejemplo en paneles digitales para tiendas, que requieren un desplazamiento continuo de datos.

Preguntas Frecuentes

Los asistentes de IA, como GitHub Copilot y ChatGPT, generan estructuras de nodos, insertadores basados โ€‹โ€‹en malloc y bucles de recorrido seguros. Los desarrolladores revisan el cรณdigo generado para comprobar que las condiciones de terminaciรณn y la liberaciรณn de memoria sean correctas antes de integrarlo en las estructuras de datos de producciรณn.

Los sistemas de aprendizaje automรกtico utilizan bรบferes circulares construidos sobre listas enlazadas circulares para almacenar ventanas deslizantes de datos en tiempo real, muestras de bรบfer de reproducciรณn para agentes de aprendizaje por refuerzo y colas cรญclicas para trabajadores productor-consumidor que alimentan lotes de entrenamiento.

Una lista enlazada simple termina con un puntero nulo, mientras que el รบltimo nodo de una lista enlazada circular apunta al primero. Este ciclo cerrado elimina las comprobaciones de nulidad al final y permite un recorrido continuo y envolvente en un solo bucle.

Una lista doblemente enlazada circular tiene dos punteros por nodo โ€”siguiente y anteriorโ€” y ambos extremos se enlazan entre sรญ. Esta estructura permite el recorrido bidireccional y bรบsquedas que, en el peor de los casos, no superan la mitad de la longitud de la lista.

El algoritmo de la tortuga y la liebre de Floyd utiliza dos punteros que se mueven a velocidades diferentes. Si se encuentran, existe un ciclo. Se ejecuta en tiempo O(n) y requiere espacio adicional O(1), y es la soluciรณn estรกndar para la detecciรณn de ciclos en entrevistas de trabajo.

La inserciรณn o eliminaciรณn en la posiciรณn actual de una lista enlazada circular se ejecuta en O(1). OperaLas operaciones que apuntan a un valor o รญndice especรญfico se ejecutan en O(n) porque la lista debe recorrerse para localizar el nodo de destino.

OperaLos planificadores de sistemas de tokens los utilizan para la planificaciรณn de CPU por turnos, las redes de anillo de tokens pasan el control entre estaciones, los reproductores multimedia recorren listas de reproducciรณn y los sistemas embebidos utilizan bรบferes circulares respaldados por listas circulares para flujos de sensores.

Los errores comunes incluyen olvidar actualizar ambos punteros de punto final despuรฉs de la inserciรณn o eliminaciรณn, omitir una condiciรณn de terminaciรณn y perderping indefinidamente, liberando un nodo sin volver a enlazar a sus vecinos y provocando fugas de memoria cuando se descarta la lista.

Resumir este post con: