Las listas circulares

Septiembre 2017


Requisitos

Los requisitos son los siguientes: los tipos de datos, las estructuras, el uso de typedef, los punteros, la función usuario, las listas enlazadas simples y las listas doblemente enlazadas.

Introducción

El objetivo de este artículo es que el lector pueda entender lo que son las listas circulares.

Definición

La lista circular es una especie de lista enlazada simple o doblemente enlazada, pero que posee una característica adicional para el desplazamiento dentro de la lista: esta no tiene fin.
Para que la lista sea sin fin, el puntero siguiente del último elemento apuntará hacia el primer elemento de la lista en lugar de apuntar al valor NULL, como hemos visto en el caso de listas enlazadas simples o doblemente enlazadas.

En las listas circulares, nunca se llega a una posición en la que ya no sea posible desplazarse. Cuando se llegue al último elemento, el desplazamiento volverá a comenzar desde el primer elemento.


Cómo construir el modelo de un elemento de la lista

Para definir un elemento de la lista, el tipo struct será utilizado. El elemento de la lista va a tener un campo dato y un puntero siguiente. Este debe ser del mismo tipo que el elemento, en caso contrario no podrá apuntar hacia el elemento. El puntero siguiente permitirá el acceso al próximo elemento.

typedef struct ElementoLista {
char *dato;
struct ElementoLista *siguiente;
}Elemento;


Para tener el control de la lista es mejor guardar ciertos elementos: el primer elemento, el último elemento y el número de elementos. Para ello, otra estructura será utilizada (no es obligatorio, pueden ser utilizadas variables).

typedef struct ListaIdentificar {
Elemento *inicio;
Elemento *fin;
int tamaño;
}Lista;


El puntero inicio albergará la dirección del primer elemento de la lista.
El puntero fin contendrá la dirección del último elemento de la lista.
La variable tamaño contiene el número de elementos.

Cualquiera que sea la posición en la lista, los punteros inicio y fin siempre apuntarán hacia el primer y el último elemento respectivamente.
El campo tamaño contendrá el número de elementos de la lista cualquiera sea la operación efectuada sobre la lista.

Operaciones sobre las listas circulares

Inicialización

Modelo de la función:

void inicialización (Lista *lista);


Esta operación debe ser realizada primero antes de cualquier otra operación sobre la lista. Comienza colocando el puntero inicio y fin con el puntero NULL, y el tamaño con el valor 0.

La función:

void inicialización (Lista *lista){
lista->inicio = NULL;
lista->fin = NULL;
tamaño = 0;
}

Inserción de un elemento en la lista

Enseguida, el algoritmo de inserción y el registro de los elementos: afirmación del elemento que se insertará, asignación de la memoria destinada al nuevo elemento, llenar el contenido del campo de los datos, actualizar los punteros hacia el primer y último elemento si es necesario.

Caso particular: en una lista con un solo elemento, el primer elemento es al mismo tiempo el último. Luego, se tiene que actualizar el tamaño de la lista.

Inserción en una lista vacía

Modelo de la función:

int ins_lista_circ_vacia(Lista * lista, char *dato);


La función devuelve -1 en caso de falla, si no devuelve 0.

Etapas: asignación de memoria para el nuevo elemento, rellenar el campo de datos del elemento nuevo, el puntero siguiente del nuevo elemento apuntará hacia si mismo (es la etapa que vuelve a la lista circular), los punteros inicio y fin apuntarán hacia el nuevo elemento, el tamaño es actualizado.


La función:

/* inserción en una lista vacía */
int ins_lista_circ_vacia(Lista * lista, char *dato){
Elemento *nuevo_elemento;
if ((nuevo_elemento = (Elemento *) malloc (sizeof (Elemento)))
== NULL)
return -1;
if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char)))
== NULL)
return -1;
strcpy (nuevo_elemento->dato, dato);

nuevo_elemento->siguiente = nuevo_elemento;
lista->inicio = nuevo_elemento;
lista->fin = nuevo_elemento;
lista->tamaño++;
return 0;
}

Inserción en una lista no vacía

Modelo de la función:

int ins_lista_circ(Lista * lista, Elemento *actual, char *dato);


La función devuelve -1 en caso de equivocación, si no devuelve 0.

La inserción se efectuará al final de la lista.

Etapas: asignación de memoria para el nuevo elemento, rellenar el campo de datos del reciente elemento, el puntero siguiente del nuevo elemento apunta hacia la dirección del primer elemento (conservar la lista circular), el puntero inicio no cambia, el puntero fin apunta hacia el nuevo elemento, el tamaño se incrementa en una unidad.



La función:

/* inserción en una lista no vacía */
int ins_lista_circ(Lista * lista, Elemento *actual, char *dato){
Elemento *nuevo_elemento;
if ((nuevo_elemento = (Elemento *) malloc (sizeof (Elemento)))
== NULL)
return -1;
if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char)))
== NULL)
return -1;
strcpy (nuevo_elemento->dato, dato);

if(actual != lista->fin)
return -1;

nuevo_elemento->siguiente = actual->siguiente;
actual->siguiente = nuevo_elemento;
lista->fin = nuevo_elemento;
lista->tamaño++;
return 0;
}

Eliminación de un elemento en la lista

En seguida, el algoritmo de eliminación de un elemento de la lista: uso de un puntero temporal para guardar la dirección de los elementos que se van a eliminar, el elemento a eliminar se encontrará después del elemento actual.

Apunta el puntero siguiente del elemento actual hacia la dirección del puntero siguiente del elemento que se eliminará, libera la memoria ocupada por el elemento eliminado yactualiza el tamaño de la lista.

Para eliminar un elemento de la lista hay varias situaciones: eliminación dentro de la lista y eliminación del último elemento de la lista.

Eliminación al inicio de la lista

Modelo de la función:

int sup_list_circ(Lista *lista);


La función devuelve -1 en caso de un error, si no devuelve 0.

Etapas: el puntero sup_elemento contendrá la dirección del elemento 1, el puntero inicio apuntará hacia el segundo elemento, el puntero siguiente del último elemento apuntará hacia el primer elemento (que era el segundo antes de la eliminación), el tamaño de la lista disminuirá 1 elemento.



La función:

/* eliminación al inicio de la lista */
int sup_lista_circ(Lista * lista){
if (lista->tamaño < 2)
return -1;
Elemento *sup_element;

sup_elemento = lista->inicio;
lista->inicio = lista->inicio->siguiente;
lista->fin->siguiente = lista->inicio;

free (sup_elemento->dato);
free (sup_elemento);
lista->tamaño--;
return 0;
}

Eliminación en una lista con un solo elemento

Modelo de la función:

int sup_list_circ_unica(Lista *lista);


La función devuelve -1 en caso de algún error, si no devuelve 0.

Etapas: el puntero sup_elemento contendrá la dirección del elemento (la lista contiene un solo elemento), el puntero inicio apuntará hacia NULL, el puntero fin apuntará hacia NULL y el tamaño de la lista disminuirá un elemento.



La función:

/* eliminación en una lista con un solo elemento*/
int sup_lista_circ_unica(Lista *lista){
if (lista->tamaño != 1)
return -1;
Elemento *sup_elemento;

sup_elemento = lista->inicio;
lista->inicio = NULL;
lista->fin = NULL;

free (sup_elemento->dato);
free (sup_elemento);
lista->tamaño--;
return 0;
}

Mostrar la lista

Para mostrar la lista completa, es necesario posicionarse al inicio de la lista (el puntero inicio lo permitirá). Después, utilizando el puntero siguiente de cada elemento, la lista es recorrida del primer al último elemento.

En comparación con las listas simples y doblemente enlazadas, en el que la condición para detenerse esta dada por el puntero siguiente del último elemento, que vale NULL, para la lista circular, no hay punto de detención, a menos que elijamos uno.

A continuación dos variantes de visualización: mostrar la lista (del primer al último elemento) y mostrar la lista sin una condición para detenerse.

Mostrar la lista (del primer al último elemento)

Utilizaremos el tamaño de la lista como la condición para detenerse.

La función:

/* mostrar la lista */
void mostrar (Lista * lista){
Elemento *actual;
actual = lista->inicio;
int i;
for(i=0;i<lista->tamaño;++i){
printf ("%p - %s\n", actual, actual->dato);
actual = actual->siguiente;
}
}

Mostrar la lista sin una condición para detenerse (indefinidamente)

La función:

/* recorrer la lista indefinidamente*/
void mostrar_indefinidamente (Lista * lista){
Elemento *actual;
actual = lista->inicio;
while (1){
printf ("%p - %s\n", actual, actual->dato);
actual = actual->siguiente;
}
}

Destrucción de la lista

Para destruir la lista completa, he utilizado al eliminación al inicio de la lista ya que el tamaño es mayor a 1, luego la eliminación en una lista con un solo elemento.

La función:

/* destruir lista */
void destruir (Lista * lista){
while (lista->tamaño > 0){
if(lista->tamaño > 1)
sup_lista_circ (lista);
else
sup_lista_circ_unica(lista);
}
}

Ejemplo completo

lista_circ.h

/* ---------- lista_circ.h ----------- */
typedef struct ElementoListaCirc {
char *dato;
struct ElementoListaCirc *siguiente;
} Element;

typedef struct ListaIdentificarCirc {
Elemento *inicio;
Elemento *fin;
int tamaño;
} Lista;

/* inicialización de la lista */
void inicialización (Lista * lista);

/* INSERCION */
/* inserción en una lista vacia */
int ins_lista_circ_vacia(Lista * lista, char *dato);
int ins_lista_circ(Lista * lista, Elemento *actual, char *dato);

/* ELIMINACION */
int sup_lista_circ (Lista * lista);
int sup_lista_circ_unica (Lista * lista);

int menu (Lista *lista);
void mostrar (Lista * lista);
void mostrar_infinito (Lista * lista);
void destruir (Lista * lista);
/* -------- FIN lista_circ.h --------- */

lista_circ_function.h

/******************************\

  • lista_circ_function.h *\******************************/void inicialización (Lista * lista){ lista->inicio = NULL; lista->fin = NULL; lista->tamaño = 0;}/* inserción en una lista vacia */int ins_lista_circ_vacia(Lista * lista, char *dato){ Elemento *nuevo_elemento; if ((nuevo_elemento = (Elemento *) malloc (sizeof (Elemento))) == NULL) return -1; if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; strcpy (nuevo_elemento->dato, dato); nuevo_elemento->siguiente = nuevo_elemento; lista->inicio = nuevo_elemento; lista->fin = nuevo_elemento; lista->tamaño<gras>; return 0;}/* inserción en una lista no vacia */int ins_lista_circ(Lista * lista, Elemento *actual, char *dato){ Elemento *nuevo_elemento; if ((nuevo_elemento = (Elemento *) malloc (sizeof (Elemento))) == NULL) return -1; if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; strcpy (nuevo_elemento->dato, dato); if(actual != lista->fin) return -1; nuevo_elemento->siguiente = actual->siguiente; actual->siguiente = nuevo_elemento; lista->fin = nuevo_elemento; lista->tamaño<gras>; return 0;}/* eliminación al inicio de la lista */int sup_lista_circ(Lista * lista){ if (lista->tamaño < 2) return -1; Elemento *sup_elemento; sup_elemento = lista->inicio; lista->inicio = lista->inicio->siguiente; lista->fin->siguiente = lista->inicio; free (sup_elemento->dato); free (sup_elemento); lista->tamaño--; return 0;}/* eliminación en una lista con un solo elemento*/int sup_lista_circ_unica(Lista *lista){ if (lista->tamaño != 1) return -1; Elemento *sup_elemento; sup_elemento = lista->inicio; lista->inicio = NULL; lista->fin = NULL; free (sup_elemento->dato); free (sup_elemento); lista->tamaño--; return 0;}/* mostrar la lista */void mostrar (Lista * lista){ Elemento *actual; actual = lista->inicio; int i; for(i=0;i<lista->tamaño;++i){ printf ("%p - %s\n", actual, actual->dato); actual = actual->siguiente; }}/* recorrer la lista indefinidamente*/void mostrar_infinito (Lista * lista){ Elemento *actual; actual = lista->inicio; while (1){ printf ("%p - %s\n", actual, actual->dato); actual = actual->siguiente; }}/* destruir esta lista */void destruir (Lista * lista){ while (lista->tamaño > 0){ if(lista->tamaño > 1) sup_lista_circ (lista); else sup_lista_circ_unica(lista); }}int menu (Lista *lista){ int elección; printf("********** MENU **********\n"); if (lista->tamaño == 0){ printf ("1. Adicion del 1er elemento\n"); printf ("2. Quitar\n"); }else { printf ("1. Adición de un elemento\n"); printf ("2. Eliminación al inicio (la lista debe tener al menos 2 elementos)\n"); printf ("3. Eliminación en una lista con un solo elemento\n"); printf ("4. Mostrar lista circular\n"); printf ("5. Mostrar lista circular [Ctrl-C] para quitar el programa\n"); printf ("6. Destruir la lista\n"); printf ("7. Quitar\n"); } printf ("\n\nElegir: "); scanf ("%d", &elección); getchar(); if(lista->tamaño == 0 && elección == 2) elección = 7; return elección;}/* -------- FIN lista_circ_function --------- */

lista_circ.c

/**********************\

  • lista_circ.c *\**********************/#include <stdio.h>#include <stdlib.h>#include <string.h>#include "lista_circ.h"#include "lista_circ_function.h"int main (void){ char elección; char *nom; Lista *lista; Elemento *actual; if ((lista = (Lista *) malloc (sizeof (lista))) == NULL) return -1; if ((nombre = (char *) malloc (50)) == NULL) return -1; actual = NULL; elección = 'o'; inicialización (lista); while (elección != 7){ elección = menu (lista); switch (elección){ case 1: printf ("Ingrese un elemento: "); scanf ("%s", nombre); getchar (); if(lista->tamaño == 0) ins_lista_circ_vacia (lista,nombre); else ins_lista_circ (lista,lista->fin,nombre); printf ("%d elements:deb=%s, fin=%s\n", lista->tamaño, lista->inicio->dato, lista->fin->dato); mostrar (lista); break; case 2: if(lista->tamaño < 2) break; sup_lista_circ (lista); if (lista->tamaño != 0) printf ("%d elements:deb=%s, fin=%s\n", lista->tamaño, lista->inicio->dato, lista->fin->dato); mostrar(lista); break; case 3: if(lista->tamaño != 1) break; sup_lista_circ_unica(lista); printf("La lista está vacia\n"); break; case 4: mostrar(lista); break; case 5: mostrar_infinito(lista); break; case 6: destruir (lista); printf ("la lista ha sido destruida: %de elementos\n", lista->tamaño); break; } } return 0;}

Consulta también

Publicado por Carlos-vialfa. Última actualización: 8 de octubre de 2016 a las 18:52 por Carlos-vialfa.
El documento «Las listas circulares» se encuentra disponible bajo una licencia Creative Commons. Puedes copiarlo o modificarlo libremente. No olvides citar a CCM (es.ccm.net) como tu fuente de información.