Escolar Documentos
Profissional Documentos
Cultura Documentos
Implementacin
Para representar en lenguaje C esta estructura de datos se utilizarn punteros, un tipo
de datos que suministra el lenguaje. Se representar una lista vaca con la constante
NULL. Se puede definir la lista enlazada de la siguiente manera:
struct lista
{
int clave;
struct lista *sig;
};
{
/* Reserva memoria para un nodo */
p = (struct lista *) malloc(sizeof(struct lista));
p->clave = i; /* Introduce la informacion */
p->sig = L; /* reorganiza */
L = p;
/* los enlaces */
}
return 0;
}
- Recorrido de una lista.
La idea es ir avanzando desde el primer elemento hasta encontrar la lista vaca. Antes
de acceder a la estructura lista es fundamental saber si esa estructura existe, es decir,
que no est vaca. En el caso de estarlo o de no estar inicializada es posible que el
programa falle y sea difcil detectar donde, y en algunos casos puede abortarse
inmediatamente la ejecucin del programa, lo cual suele ser de gran ayuda para la
depuracin.
Como se ha dicho antes, la lista enlazada es una estructura recursiva, y una posibilidad
para su recorrido es hacerlo de forma recursiva. A continuacin se expone el cdigo de
un programa que muestra el valor de la clave y almacena la suma de todos los valores
en una variable pasada por referencia (un puntero a entero). Por el hecho de ser un
proceso recursivo se utiliza un procedimiento para hacer el recorrido. Ntese como
antes de hacer una operacin sobre el elemento se comprueba si existe.
int main(void)
{
struct lista *L;
struct lista *p;
int suma;
L = NULL;
/* crear la lista */
...
suma = 0;
recorrer(L, &suma);
return 0;
}
void recorrer(struct lista *L, int *suma)
{
if (L != NULL) {
printf("%d, ", L->clave);
*suma = *suma + L->clave;
recorrer(L->sig, suma);
}
}
int main(void)
{
struct lista *L;
struct lista *p;
int suma;
L = NULL;
/* crear la lista */
...
p = L;
suma = 0;
while (p != NULL) {
printf("%d, ", p->clave);
suma = suma + p->clave;
p = p->sig;
}
return 0;
}
A menudo resulta un poco difcil de entender la instruccin p = p->sig; Simplemente
cambia la direccin actual del puntero p por la direccin del siguiente enlace. Tambin
es comn encontrar instrucciones del estilo: p = p->sig->sig; Esto puede traducirse en
dos instrucciones, de la siguiente manera:
p = p->sig;
p = p->sig;
Obviamente slo debe usarse cuando se sepa que p->sig es una estructura no vaca,
puesto que si fuera vaca, al hacer otra vez p = p->sig se producira una referencia a
memoria no vlida.
Y si queremos insertar en una posicin arbitraria de la lista o queremos borrar un
elemento? Como se trata de operaciones algo ms complicadas (tampoco mucho) se
expone su desarrollo y sus variantes en los siguientes tipos de listas: las listas
ordenadas y las listas reorganizables. Asimismo se estudiarn despus las listas que
incorporan cabecera y centinela. Tambin se estudiarn las listas con doble enlace.
Todas las implementaciones se harn de forma iterativa, y se deja propuesta por ser
ms sencilla su implementacin recursiva, aunque es recomendable utilizar la versin
iterativa.
Listas ordenadas
Las listas ordenadas son aquellas en las que la posicin de cada elemento depende de
su contenido. Por ejemplo, podemos tener una lista enlazada que contenga el nombre
y apellidos de un alumno y queremos que los elementos -los alumnos- estn en la lista
en orden alfabtico.
La creacin de una lista ordenada es igual que antes:
struct lista *L;
L = NULL;
Cuando haya que insertar un nuevo elemento en la lista ordenada hay que hacerlo en
el lugar que le corresponda, y esto depende del orden y de la clave escogidos. Este
}
void insertar(struct lista **L, int elem)
{
struct lista *actual, *anterior, *nuevo;
*/
nuevo->sig = anterior;
*L = nuevo; /* importante: al insertar al principio actuliza la
cabecera */
}
else {
nuevo->sig = actual;
anterior->sig = nuevo;
}
}
Se puede apreciar que se pasa la lista L con el parmetro **L . La razn para hacer
esto es que cuando se inserta al comienzo de la lista (porque est vaca o es donde
corresponde) se cambia la cabecera.
Un ejemplo de prueba: suponer que se tiene esta lista enlazada: 1 -> 3 -> 5 -> NULL
Queremos insertar un 4. Al hacer la bsqueda el puntero actual apunta al 5. El
puntero anterior apunta al 3. Y nuevo contiene el valor 4. Como no se inserta al
principio se hace que el enlace siguiente a nuevo sea actual, es decir, el 5, y el enlace
siguiente a anterior ser nuevo, es decir, el 4.
La mejor manera de entender el funcionamiento es haciendo una serie de seguimientos
a mano o con la ayuda del depurador.
A continuacin se explica el borrado de un elemento. El procedimiento consiste en
localizarlo y borrarlo si existe. Aqu tambin se distingue el caso de borrar al principio o
borrar en cualquier otra posicin. Se puede observar que el algoritmo no tiene ningn
problema si el elemento no existe o la lista est vaca.
void borrar(struct lista **L, int elem)
{
struct lista *actual, *anterior;
/* 1.- busca su posicion. Es casi igual que en la insercion, ojo al (<)
*/
anterior = actual = *L;
while (actual != NULL && actual->clave
< elem) {
anterior = actual;
actual = actual->sig;
}
/* 2.- Lo borra si existe */
if (actual != NULL && actual->clave == elem) {
if (anterior == actual)
/* borrar el primero */
*L = actual->sig;
/* o tambien (*L)->sig; */
else
/* borrar en otro sitio */
anterior->sig = actual->sig;
free(actual);
}
}
Listas reorganizables
Las listas reorganizables son aquellas en las que cada vez que se accede a un
elemento ste se coloca al comienzo de la lista. Si el elemento al que se accede no
est en la lista entonces se aade al comienzo de la misma. Cuando se trata de borrar
un elemento se procede de la misma manera que en la operacin de borrado de la lista
ordenada. Notar que el orden en una lista reorganizable depende del acceso a un
elemento, y no de los valores de las claves.
No se va a desarrollar el procedimiento de insercin / acceso en una lista, se deja
como ejercicio. De todas formas es sencillo. Primero se busca ese elemento, si existe
se pone al comienzo de la lista, con cuidado de no perder los enlaces entre el
elemento anterior y el siguiente. Y si no existe pues se aade al principio y ya est. Por
ltimo se actualiza la cabecera.
Se declara una lista vaca con cabecera, reservando memoria para la cabecera, de la
siguiente manera:
struct lista {
int clave;
struct lista *sig;
}
...
struct lista *L;
L = (struct lista *) malloc(sizeof(struct lista));
L->sig = NULL;
Antes de implementar el proceso de insercin en una lista con cabecera, se explicar el
uso del centinela, y se realizarn los procedimientos de insercin y borrado
aprovechando ambas ideas.
El centinela es un elemento que se aade al final de la estructura, y sirve para acotar
los elementos de informacin que forman la lista. Pero tiene otra utilidad: el lector
habr observado que a la hora de buscar un elemento de informacin, ya sea en la
Procedimiento de insercin:
void insertarLCC(struct listacc LCC, int elem)
{
Ejemplo de uso:
#include <stdio.h>
#include <stdlib.h>
struct lista
{
int clave;
struct lista *sig;
};
struct listacc
{
struct lista *cabecera,
*centinela;
};
void crearLCC(struct listacc *LCC);
- Declaracin:
struct listaDE
{
int clave;
struct listaDE *ant,
*sig;
};
- Procedimiento de creacin:
void crearDE(struct listaDE **LDE)
{
*LDE = (struct listaDE *) malloc(sizeof(struct listaDE));
(*LDE)->sig = (*LDE)->ant = *LDE;
}
- Procedimiento de insercin:
void insertarDE(struct listaDE *LDE, int elem)
{
struct listaDE *actual, *nuevo;
/* busca */
actual = LDE->sig;
LDE->clave = elem;
while (actual->clave < elem)
actual = actual->sig;
/* crea */
nuevo = (struct listaDE *) malloc(sizeof(struct listaDE));
nuevo->clave = elem;
/* enlaza */
actual->ant->sig = nuevo;
nuevo->ant = actual->ant;
nuevo->sig = actual;
actual->ant = nuevo;
}
- Procedimiento de borrado:
void borrarDE(struct listaDE *LDE, int elem)
{
struct listaDE *actual;
/* busca */
actual = LDE->sig;
LDE->clave = elem;
while (actual->clave < elem)
actual = actual->sig;
/* borra */
if (actual != LDE && actual->clave == elem) {
actual->sig->ant = actual->ant;
actual->ant->sig = actual->sig;
free(actual);
}
}
Listas circulares
Las listas circulares son aquellas en las que el ltimo elemento tiene un enlace con el
primero. Su uso suele estar relacionado con las colas, y por tanto su desarrollo se
realizar en el tema de colas. Por supuesto, se invita al lector a desarrollarlo por su
cuenta.
Problemas propuestos: