Listas enlazadas A.J.M.Checa<[email protected]> February 5, 2008 IMPORTANTE: La Práctica planteada en la última sección de estos apuntes supone un 50% de la nota final. Será entregada y explicada en una de las dos convocatorias de febrero de 2008. 1 Introducción Las listas enlazadas son elementos del mismo tipo enlazados mediante una dirección a la que llamaremos nodo. En nuestro caso, los elementos serán estructuras y los nodos punteros al mismo tipo de estructura. En la figura 1 se representa el primer elemento de una lista, al que Figure 1: Primer elemento hemos denominado ini1. c1, c2,...,son los campos del elemento que contendrán información y nodo es un campo especial, ya que ha de contener la dirección del siguiente elemento, al que hemos denominado act. En la figura 2 se representa una lista enlazada. El acceso a cualquiera de sus elementos es secuencial, ya que para llegar hasta él hemos de recorrer los que le preceden. 1 Es importante recordar que estos nombre se refieren a direcciones asignadas mediante la función malloc a las estructuras que formarán la lista enlazadas. 1 Figure 2: Lista enlazada 2 Generación de una lista simplemente enlazada Una lista es simplemente enlazada cuando cada elemento posee un único nodo que enlaza con el siguiente elemento. En primer lugar, antes de enlazar el primero con el segundo, hemos de tener ambos: como Figure 3: Los dos primeros elementos sin enlazar se muestra en la figura 3. A continuación enlazamos el primero con el segundo; aunque esto mismo lo hubiéramos podido hacer sin utilizar act: 2 ini -> sig = (struct *lista) malloc(sizeof(struct lista)); Sin embargo nosotros seguiremos utilizando el puntero act, pues como veremos a continuación, resulta muy útil para generar una lista. 2 Figure 4: Los dos primeros elementos enlazados Enlazaremos el segundo con un tercero, previa generación de un nuevo elemento que llamaremos new; 4 5 new = (struct *lista) malloc(sizeof(struct lista)); act -> sig = new; pero hacer esto es equivalente a; 4 act -> sig = (struct *lista) malloc(sizeof(struct lista)); con lo cual no es necesario introducir un nuevo puntero a estructura new. Ya estamos en condiciones de generar automáticamente una lista enlazada de N elementos. No olvidemos que la lista es de acceso secuencial, lo cual quiere decir que solo es necesario conocer la dirección del primer elemento de la lista; for(i=0 , i<N-1, i++) { act -> sig = (struct *lista) malloc(sizeof(struct lista)); act = act -> sig; } La sentencia act = act -> sig; es para actualizar el elemento actual. No se pierde ninguna información, ya que los nodos se van guardando en elementos anteriores. Por otra parte, este bucle se realiza N − 1 veces, pues ya tenemos el primer elemento ini. 3 El código completo lo resumimos en el siguiente cuadro mediante la declaración de una determinada estructura con un único campo; #include <stdlib.h> struct lista{ int n; struct lista *sig; } *ini,*act; main(){ int i; ini = (struct *lista) malloc(sizeof(struct lista)); act=ini; for(i=0 , i<N-1, i++) { act -> sig = (struct *lista) malloc(sizeof(struct lista)); act = act -> sig; } act -> sig = NULL; return 0; } El último comando act -> sig = NULL; es útil para cerrar la lista, o lo que es lo mismo, para saber cual es el último elemento. 2.1 Ejercicio a. Implemente una función que permita eliminar o insertar un determinado elemento en una lista simplemente enlazada, siempre que no sea el primero o el último. b. Como en el caso anterior, pero teniendo en cuenta si el elemento es el primero o el último. 4 3 Generación listas doblemente enlazada Decimos que una lista está doblemente enlazada cuando cada elemento posee dos nodos, uno que contiene la dirección del siguiente elemento y otro que contiene la dirección del elemento anterior. struct lista{ int n; struct lista *sig, *ant; } *ini,*act,*ult; La estructura anterior puede utilizarse para crear la lista enlazada de la figura 5.En este caso particular la lista se dice que es circular, pues el último elemento de la lista ult contiene la dirección del primero ini, y a su vez el primero se enlaza con ult, y como es circular, hemos de Figure 5: Lista circular doblemente enlazada guardar dos direcciones, ini y ult. El siguiente programa puede utilizarse para implementar una lista no circular doblemente enlazada. #include <stdlib.h> struct lista{ int n; struct lista *sig, *ant; } *ini,*act,*new; int main(){ int i; ini = (struct *lista) malloc(sizeof(struct lista)); 5 ini -> ant = NULL; act=ini; for(i=0 ; { act -> (act -> act = } i<N-1; i++) sig = (struct *lista) malloc(sizeof(struct lista)); sig) -> ant = act; act -> sig ; act -> sig = NULL; return 0; } 4 Añadir y eliminar elementos de una lista Estas notas solo son indicativas respecto a la inserción o extracción de elementos de una lista doblemente enlazada. No tiene en cuenta como hacerlo si se trata del primer o último elemento. Esta tarea ha de ser desarrollada por el alumno como parte de la práctica que se detalla en la introducción. la siguiente función permite añadir un elemento en una lista ordenada por el campo n, /* Esta función inserta un nuevo elemento (new =malloc(sizeof(struct lista)); ) entre los elementos menor y mayor que Nel. Nel se compara con el contenido del campo n del elemento actual y con el contenido del campo n del elemento siguiente mediante la instrucción if( (Nel > act->n )&&(Nel < (act->sig)->n )).*/ #include <stdlib.h> struct lista{ int n; struct lista *sig, *ant; } *ini,*act,*new; 6 void aniadirElemento(struct lista *act,int Nel){ while(act!=NULL) { if( (Nel > act->n )&&(Nel < (act->sig)->n )){ new =malloc(sizeof(struct lista)); new->n=Nel; new->sig=act->sig; new->ant=act; (act->sig)->ant=new; act->sig=new; return; } act=act->sig; } return; } } } La sentencia while(act! = N U LL) es válida para una lista que no se circular, pues el último elemento de la lista es el único con dirección nula. la siguiente función permite añadir un elemento en una lista ordenada por el campo n, /* Esta función elimina el elemento cuyo campo n sea Nel*/ 7 void borrarElemento(struct lista *act,int Nel){ while(act!=NULL) { if(act->n==Nel){ (act->ant)->sig=act->sig; (act->sig)->ant=act->ant; free(act); return; } act=act->sig; } return; } } } 5 Práctica Escriba un programa en C para crear un lista circular doblemente enlazada con dos opciones2 a. Eliminar un determinado elemento de la lista. b. Añadir un nuevo elemento. 2 Tenga cuidado si se trata del primer o último elemento. 8