Está en la página 1de 53

ESTRUCTURAS DINMICAS DE DATOS

EN LENGUAJE C

Realizado por
Ismael Camarero
Revisado Msc. Ral H Ruiz C

Junio de 2008.
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Generalidades:
Introduccin:

Hasta ahora, hemos empleado diferentes tipos de datos: int, char, float y agrupaciones de estos como
pueden ser los arrays, struct Sin embargo, pese a su utilidad, presentan un problema: deben ser
declarados en todo su tamao. Si declaramos un array como:

float array[100[100];

estamos declarando un array de 100x100 elementos flotantes que van a ocupar

100x100xsizeof(float) bytes de memoria.

El tamao se decide en la declaracin de la variable, no en el momento en que lo necesitamos.


Pero qu sucede si slo queremos guardar un elemento? Que desaprovechamos el resto de la memoria
reservada para 10000 elementos float. Es un derroche que, aunque con las memorias actuales se pueda
permitir, no debemos hacer.
Adems qu sucedera si en un momento necesitamos cambiar la cantidad de datos a almacenar?

Una de las aplicaciones ms interesantes y potentes de la memoria dinmica y los punteros son las
estructuras dinmicas de datos. Las estructuras bsicas disponibles en C y C++ tienen una importante
limitacin: no pueden cambiar de tamao durante la ejecucin. Los arreglos estn compuestos por un
determinado nmero de elementos, nmero que se decide en la fase de diseo, antes de que el programa
ejecutable sea creado.
En muchas ocasiones se necesitan estructuras que puedan cambiar de tamao durante la ejecucin del
programa. Por supuesto, podemos hacer 'arrays' dinmicos, pero una vez creados, tu tamao tambin
ser fijo, y para hacer que crezcan o diminuyan de tamao, deberemos reconstruirlas desde el principio
(funciones calloc(), malloc() y realloc())

Vamos a ver una nueva forma de gestionar datos: ESTRUCTURAS DINAMICAS DE DATOS

Las estructuras dinmicas nos permiten crear estructuras de datos que se adapten a las necesidades reales
a las que suelen enfrentarse nuestros programas. Pero no slo eso, como veremos, tambin nos
permitirn crear estructuras de datos muy flexibles, ya sea en cuanto al orden, la estructura interna o las
relaciones entre los elementos que las componen.
Las estructuras de datos estn compuestas de otras pequeas estructuras a las que llamaremos nodos o
elementos, que agrupan los datos con los que trabajar nuestro programa y adems uno o ms punteros
autoreferenciales, es decir, punteros a objetos del mismo tipo tipo.

3
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Dentro de los datos de este tipo de datos podemos hablar de:


Listas.
Pilas.
Colas.
rboles.
Grafos

LISTAS ENLAZADAS

Una lista lineal enlazada es un conjunto de elementos u objetos de cualquier tipo, originariamente vaca
que, durante la ejecucin del programa va creciendo o decreciendo elemento a elemento segn las
necesidades previstas.
En una lista lineal cada elemento apunta al siguiente, es decir, cada elemento tiene informacin de dnde
esta el siguiente. Por este motivo tambin se le llama lista enlazada.
Grficamente se puede representar en la forma:

c
sig sig sig NULL

Las estructuras dinmicas son una implementacin de TDAs o TADs (Tipos Abstractos de Datos).

Dependiendo del nmero de punteros y de las relaciones entre nodos, podemos distinguir varios tipos
de estructuras dinmicas:
LISTAS SIMPLEMENTE ENLAZADA (o abiertas):

Cada elemento (nodo) slo dispone de un puntero, que apuntar al siguiente elemento de la lista o
valdr NULL si es el ltimo elemento. Slo se pueden recorrer hacia delante.

4
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

PILAS:

Son un tipo especial de lista, conocidas como listas LIFO (Last In, First Out): el ltimo en entrar es el
primero en salir). Los elementos se "amontonan" o apilan, de modo que slo el elemento que est
encima de la pila puede ser ledo, y slo pueden aadirse elementos encima de la pila.

COLAS:

Otro tipo de listas, conocidas como listas FIFO (First In, First Out: El primero en entrar es el primero en
salir). Los elementos se almacenan en fila, pero slo pueden aadirse por un extremo y leerse por el otro.

LISTAS CIRCULARES:

Tambin llamadas listas cerradas, son parecidas a las listas enlazadas, pero el ltimo elemento apunta al
primero. De hecho, en las listas circulares no puede hablarse de "primero" ni de "ltimo". Cualquier
nodo puede ser el nodo de entrada y salida. Se recorren siempre en el mismo sentido.

LISTAS DOBLEMENTE ENLAZADAS:

Cada elemento dispone de dos punteros, uno apunta al siguiente elemento y el otro al elemento anterior.
Al contrario que las listas abiertas anteriores, estas listas pueden recorrerse en los dos sentidos.

5
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Listas simplemente enlazadas


La forma ms simple de estructura dinmica es la lista simplemente enlazada o lista abierta.

En esta forma los nodos se organizan de modo que cada uno apunta al siguiente, y el ltimo no apunta a
nada, es decir, el puntero del nodo siguiente vale NULL:

c
sig NULL
sig sig

La anterior es una lista simplemente enlazada que consta de 4 elementos (nodos).

Para crear un alista debemos definir la clase de elementos que van a formar parte de la misma. Un tipo
de dato genrico podra ser la siguiente estructura:

struct nodo
{
int dato;
struct nodo *sig;
};

6
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

El miembro de la estructura "sig", puede apuntar a un objeto del tipo nodo. De este modo, cada nodo
puede usarse como un ladrillo para construir listas de datos, y cada uno mantendr ciertas relaciones con
otros nodos.
En el ejemplo, cada elemento de la lista slo contiene un dato de tipo entero, pero en la prctica no hay
lmite en cuanto a la complejidad de los datos a almacenar. Se pueden disear datos struct que acten
como nodos tan complejos como se desee.

El nodo anterior se representar as (no consideramos el miembro datos, slo el miembro sig, que ahora
apunta a NULL

miembro int dato

miembro struct nodo *sig

Para acceder a un nodo de la estructura slo necesitaremos un puntero a un nodo. En el ejemplo anterior
declaramos una variable de estructura, que va a ser un puntero a dicha estructura.
Mediante typedef declaramos un nuevo tipo de dato:

typedef struct s
{
int dato;
struct s *siguiente;
} elemento;

elemento *c; //puntero a nodo

Ahora elemento es un tipo de dato, sinnimo de struct s. El miembro sig ha sido declarado
como puntero al dato struct s y no como puntero a tipo de dato elemento, ya que en ese instante,
elemento no estaba an definido.
Por tanto, elemento es el tipo para declarar nodos. Ahora es un tipo de dato. La variable *c es un puntero
al tipo de dato elemento

En las listas simples enlazadas existe un nodo especial: el primero. Normalmente diremos que nuestra
lista es un puntero a ese primer nodo y llamaremos a ese nodo cabeza de la lista. Eso es porque
mediante ese nico puntero podemos acceder a toda la lista.

7
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Es muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento, ya
que si no existe ninguna copia de ese valor, y se pierde, ser imposible acceder al nodo y no podremos utilizar la
informacin almacenada ni liberar el espacio de memoria que ocupa.

Cuando el puntero que usamos para acceder a la lista vale NULL, diremos que la lista est vaca:

*c = NULL; //lista vaca

Inicialmente *sig apunta a NULL. Lo representamos grficamente en la forma:

c
NULL

La variable puntero c apunta al primer nodo. Esta lista consta de un nodo.


Si ahora hacemos que el miembro *sig guarde la direccin de memoria del siguiente nodo (diremos que
apunte al siguiente nodo), tenemos dos nodos localizables mediante
p p y p->siguiente

c sig
NULL

Operaciones bsicas con listas:

Con las listas se pueden realizar las siguientes operaciones bsicas:


a) Crear lista.
b) Aadir o insertar elementos.
c) Buscar o localizar elementos.
d) Borrar elementos.
e) Moverse a travs de una lista.
f) Ordenar una lista.

8
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Cada una de estas operaciones tendr varios casos especiales, por ejemplo, no ser lo mismo insertar un
nodo en una lista vaca, o al principio de una lista no vaca, o la final, o en una posicin intermedia.

a) Crear Lista.

Si queremos crear una lista, podemos emplear la tcnica de reservar memoria dinmicamente. Para ello
escribimos una funcin llamada nuevo_elemento:

elemento *nuevo_elemento(void)
{

elemento *q;
q = (elemento *)malloc(sizeof(elemento));
if(!q)
perror(No se ha reservado memoria para el nuevo nodo);
return q; //devuelve la direcc. de memomria reservada
}

La funcin principal tomar la forma:

int main()
{

elemento *q; //puntero a un nodo


q = NULL; //lista vaca

//Reserva dinmica de memoria


q = nuevo_elemento(); //q apunta al nodo recin creado
q->sig = NULL; //fin de lista

//operaciones. . .

//Liberacin de memoria reservada


free(q);
}

Grficamente: q

NULL

9
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Cada vez que deseemos crear un nuevo nodo, debemos llamar a la funcin nuevo_elemento() para
realizar la reserva de memoria.

b) Aadir o insertar elementos en una lista enlazada:

Veremos primero los casos sencillos y finalmente construiremos un algoritmo genrico para la insercin
de elementos en una lista.

Insertar un elemento en una lista vaca:


Este es el caso ms sencillo. Equivale a crear una lista, como en el caso a). Partiremos de que ya
tenemos el nodo a insertar (creado en la llamada a la funcin nuevo_elemento() )y, por supuesto un
puntero que apunte a l, adems el puntero a la lista valdr NULL:

El proceso es muy simple, bastar con que se realice lo siguiente:

q = nuevo_elemento();
q->siguiente = NULL;

Insertar un elemento en la primera posicin de una lista:

Podemos considerar el caso anterior como un caso particular de ste, la nica diferencia es que en el
caso anterior la lista es una lista vaca, pero siempre podemos, y debemos considerar una lista vaca
como una lista.
Por tanto, ahora la lista ya existe.
De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l ( q=nuevo elemento()), y de una lista
en este caso no vaca, apuntada por c:

c
q sig sig sig NULL

NULL

Hemos creado q mediante:

q = nuevo_elemento();

10
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Apuntamos q->sig a c:

q->sig = c;

Ahora reapuntamos c a q:
c = q;

Es fundamental no alterar el orden de las operaciones.


Grficamente, tenemos: c

q
c
NULL
sig sig sig NULL

Este sistema de insercin permite generalizar un mtodo:

/*Programa que crea una lista lineal y permite insertar datos numricos enteros en
el primer nodo. Para finalizar la introduccin de datos, pulsar EOF (en windows CRT
+ Z; en Linux CTRL +D) Antes finalizar imprime los datos introducidos, en orden
inverso al de introduccin como consecuencia de que siempre insertamos el nuevo nodo
en la cabecera de la lista.*/
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>

struct datos
{

int num;
struct datos *sig;
};

typedef datos *p;

//prototipos de funciones a utilizar.


datos *nuevo_nodo(void);
void error(void);

11
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

int main()
{

int contador = 0, n = 0;
datos *q = NULL, *c=NULL; //Lista vaca
printf("\nDato numero %d: ", contador++);
while( scanf("%d", &n) != EOF)
{
q = nuevo_nodo();
q->num = n; //num es un miembro de la estructura
q->sig = c;
c = q;
printf("\nDato numero %d: ", contador++);
} //fin de while
do
{
printf("%d\t", c->num);
}while((c = c->sig));
free(q);
getch();
}

//cuerpo de la funcin para reservar memoria


datos *nuevo_nodo(void)
{

datos *q = (datos *)malloc(sizeof(datos));


if(!q)
error();
return q;
}

void error(void)
{

perror("Memoria no reservada");
getch();
exit(1);
}

El orden de los elementos en la lista, es el inverso del orden en que han sido introducidos.

12
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Insertar un elemento en la ltima posicin de una lista:

Este es otro caso especial. Para este caso partiremos de una lista no vaca:

El proceso en este caso tampoco es excesivamente complicado:


Necesitamos un puntero que seale al ltimo elemento de la lista. La manera de conseguirlo es empezar
por el primero y avanzar hasta que el nodo que tenga como siguiente el valor NULL (es decir, el ltimo).
Hacer que nodo->siguiente sea NULL.
Hacer que ultimo->siguiente sea nodo.

Insertar un elemento a continuacin de un nodo cualquiera de una lista:

De nuevo podemos considerar el caso anterior como un caso particular de este. Ahora el nodo "anterior"
ser aquel (p) a continuacin del cual insertaremos el nuevo nodo:

NULL
p

...
sig sig

El proceso grfico es:

sig
p

...
sig sig

13
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

El fragmento de cdigo en lenguaje C que explica el proceso es:


....
q = nuevo_nodo();
q->num = n; //n es el entero que insertamos
q->sig = p->sig;
p->sig = q;

c) Localizar elementos en una lista enlazada:

Muy a menudo necesitaremos recorrer una lista, ya sea buscando un valor particular o un nodo concreto.
Las listas abiertas slo pueden recorrerse en un sentido, ya que cada nodo apunta al siguiente, pero no se
puede obtener, por ejemplo, un puntero al nodo anterior desde un nodo cualquiera si no se empieza
desde el principio.
Para recorrer una lista procederemos siempre del mismo modo, usaremos un puntero auxiliar q como
ndice:
. . . .
q = c;
printf(Valor a localizar?);
scanf(%d, &x);
while(q != NULL && q->num != x)
q = q->sig;
. . . .

El anterior razonamiento se puede emplear para visualizar todos los elementos de una lista:
. . . .
q = c;
while(q != NULL)
{
printf(%d\t, q->num);
q = q->sig;
}
. . . .
Supongamos que slo queremos mostrar los valores hasta que encontremos uno que sea mayor que 100,
podemos sustituir el bucle por:

14
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

. . . .
q = c;
while(q != NULL && q->num <100)
{
printf(%d\t, q->num);
q = q->sig;
}
. . . .

Si analizamos la condicin del bucle, tal vez encontremos un posible error: Qu pasara si ningn valor
es mayor que 100, y alcancemos el final de la lista?. Podra pensarse que cuando q sea NULL, si
intentamos acceder a q->num se producir un error.

En general eso ser cierto, no puede accederse a punteros nulos. Pero en este caso, ese acceso est dentro
de una condicin y forma parte de una expresin "and". Recordemos que cuando se evala una
expresin "and", se comienza por la izquierda, y la evaluacin se abandona cuando una de las
expresiones resulta falsa, de modo que la expresin "q->num < 100" nunca se evaluar si q es
NULL.

Si hubiramos escrito la condicin al revs, el programa nunca funcionara bien. Esto es algo muy
importante cuando se trabaja con punteros.

d) Borrar elementos de una lista enlazada:

De nuevo podemos encontrarnos con varios casos, segn la posicin del nodo a eliminar.

d1) Eliminar el primer nodo de una lista abierta:

Es el caso ms simple. Partiremos de una lista con uno o ms nodos, y usaremos un puntero auxiliar, q:
Hacemos que q apunte al primer elemento de la lista, es decir a c.
Asignamos a c la direccin del segundo nodo de la lista: c = c->siguiente.
Liberamos la memoria asignada al primer nodo, q, el que queremos eliminar: free(q);
Si no guardamos el puntero al primer nodo antes de actualizar c, despus nos resultara imposible liberar
la memoria que ocupa. Si liberamos la memoria antes de actualizar c, perderemos el puntero al segundo
nodo.

15
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Si la lista slo tiene un nodo, el proceso es tambin vlido, ya que el valor de c->siguiente es NULL, y
despus de eliminar el primer nodo la lista quedar vaca, y el valor de c ser NULL.
De hecho, el proceso que se suele usar para borrar listas completas es eliminar el primer nodo hasta que
la lista est vaca.

d2) Eliminar un nodo cualquiera de una lista enlazada:

En todos los dems casos, eliminar un nodo se puede hacer siempre del mismo modo. Supongamos que
tenemos una lista con al menos dos elementos, y un puntero p al nodo anterior al que queremos
eliminar. Y un puntero auxiliar q.

Hacemos que q apunte al nodo que queremos borrar (el siguiente a p):

q = p->sig;

Ahora, asignamos como nodo siguiente del nodo anterior, el siguiente al que queremos eliminar:

p->sig = q->sig;

Eliminamos la memoria asociada al nodo que queremos eliminar.

free(q);

Si el nodo a eliminar es el ltimo, es procedimiento es igualmente vlido, ya que p pasar a ser el


ltimo, y p->sig valdr NULL.

d3) Borrar todos los elementos de una lista

Borrar todos los elementos de una lista equivale a liberar la memoria reservada para cada uno de los
elementos de la misma. Si el primer nodo est apuntado por c, empleamos el puntero auxiliar q:
q = c; //salvamos el puntero a la lista
while(q != NULL)
{
c = c->sig;
free(q);
q = c;
}
Hay que observar que antes de borrar el elemento apuntado por q, hacemos que c apunte al siguiente
elemento ya que si no perdemos el resto de la lista

16
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

e) Moverse a travs de una lista lineal simplemente enlazada:

Slo hay un modo de moverse a travs de una lista abierta, hacia adelante.
An as, a veces necesitaremos acceder a determinados elementos de una lista abierta. Veremos ahora
como acceder a los ms corrientes: el primero, el ltimo, el siguiente y el anterior.

Primer elemento (nodo) de una lista:

El primer elemento es el ms accesible, ya que es a ese a que apunta el puntero que define la lista. Para
obtener un puntero al primer elemento bastar con copiar el puntero c.

Elemento siguiente a uno cualquiera:

Supongamos que tenemos un puntero q que seala a un elemento de una lista. Para obtener un puntero al
siguiente bastar con asignarle el campo "siguiente" del nodo q:
q = q->siguiente;

Elemento anterior a uno cualquiera:

Ya hemos dicho que no es posible retroceder en una lista lineal simplemente enlazada, de modo que
para obtener un puntero al nodo anterior a uno dado tendremos que partir del primero, e ir avanzando
hasta que el nodo siguiente sea precisamente nuestro nodo.

ltimo elemento de una lista:

Para obtener un puntero al ltimo elemento de una lista partiremos de un nodo cualquiera, por ejemplo el
primero, y avanzaremos hasta que su nodo siguiente sea NULL.

Saber si una lista est vaca:

Basta con comparar el puntero a la cabecera de la lista, c, con NULL, si c vale NULL la lista est
vaca.

17
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Borrar una lista completa:

El algoritmo genrico para borrar una lista completa consiste simplemente en borrar el primer elemento
sucesivamente mientras la lista no est vaca.

f) Ordenar una lista enlazada:

Supongamos que queremos construir una lista para almacenar nmeros enteros, pero de modo que
siempre est ordenada de menor a mayor. Para hacer la prueba aadiremos los valores 20, 10, 40, 30. De
este modo tendremos todos los casos posibles. Al comenzar, el primer elemento se introducir en una
lista vaca, el segundo se insertar en la primera posicin, el tercero en la ltima, y el ltimo en una
posicin intermedia.

Insertar un elemento en una lista vaca es equivalente a insertarlo en la primera posicin. De modo que
no incluiremos una funcin para asignar un elemento en una lista vaca, y haremos que la funcin para
insertar en la primera posicin nos sirva para ese caso tambin.

Algoritmo de insercin:

El primer paso es crear un nodo para el dato que vamos a insertar.

Si Lista es NULL, o el valor del primer elemento de la lista es mayor que el del nuevo, insertaremos el
nuevo nodo en la primera posicin de la lista.

En caso contrario, buscaremos el lugar adecuado para la insercin, tenemos un puntero "anterior". Lo
inicializamos con el valor de Lista, y avanzaremos mientras anterior->siguiente no sea NULL y el dato
que contiene anterior->siguiente sea menor o igual que el dato que queremos insertar.

Ahora ya tenemos anterior sealando al nodo adecuado, as que insertamos el nuevo nodo a continuacin
de l. El cdigo en lenguaje C de la funcin que emplearemos es:

void insertar(Lista *lista, int v)


{
pNodo nuevo, anterior;

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;

/* Si la lista est vaca */


if(listaVacia(*lista) || (*lista)->valor > v)
{

18
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

/* Aadimos la lista a continuacin del nuevo nodo */


nuevo->siguiente = *lista;
/* Ahora, el comienzo de nuestra lista es en nuevo nodo */
*lista = nuevo;
}
else {
/* Buscar el nodo de valor menor a v */
anterior = *lista;
/* Avanzamos hasta el ltimo elemento o hasta que el siguiente tenga
un valor mayor que v */
while(anterior->siguiente && anterior->siguiente->valor <= v)
anterior = anterior->siguiente;
/* Insertamos el nuevo nodo despus del nodo anterior */
nuevo->siguiente = anterior->siguiente;
anterior->siguiente = nuevo;
}
}

Algoritmo para borrar un elemento:

Despus probaremos la funcin para buscar y borrar, borraremos los elementos 10, 15, 45, 30 y 40, as
probaremos los casos de borrar el primero, el ltimo y un caso intermedio o dos nodos que no existan.
Recordemos que para eliminar un nodo necesitamos disponer de un puntero al nodo anterior.
Lo primero ser localizar el nodo a eliminar, si es que existe. Pero sin perder el puntero al nodo anterior.
Partiremos del nodo primero, y del valor NULL para anterior. Y avanzaremos mientras nodo no sea
NULL o mientras que el valor almacenado en nodo sea menor que el que buscamos.
Ahora pueden darse tres casos:
Que el nodo sea NULL, esto indica que todos los valores almacenados en la lista son menores que el que
buscamos y el nodo que buscamos no existe. Retornaremos sin borrar nada.
Que el valor almacenado en nodo sea mayor que el que buscamos, en ese caso tambin retornaremos sin
borrar nada, ya que esto indica que el nodo que buscamos no existe.
Que el valor almacenado en el nodo sea igual al que buscamos.
De nuevo existen dos casos:
Que anterior sea NULL. Esto indicara que el nodo que queremos borrar es el primero, as que
modificamos el valor de Lista para que apunte al nodo siguiente al que queremos borrar.
Que anterior no sea NULL, el nodo no es el primero, as que asignamos a anterior->siguiente la
direccin de nodo->siguiente.
Despus, liberamos la memoria de nodo. La funcin que emplearemos es la siguiente

void borrar(Lista *lista, int v)


{
pNodo anterior, nodo;

nodo = *lista;
anterior = NULL;
while(nodo && nodo->valor < v) {
anterior = nodo;
nodo = nodo->siguiente;
}

19
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

if(!nodo || nodo->valor != v) return;


else { /* Borrar el nodo */
if(!anterior) /* Primer elemento */
*lista = nodo->siguiente;
else /* un elemento cualquiera */
anterior->siguiente = nodo->siguiente;
free(nodo);
}
}

Cdigo completo del ejemplo:

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

struct nodo {
int valor;
struct nodo *siguiente;
};//tipoNodo;

typedef struct nodo tipoNodo;


typedef tipoNodo *pNodo;
typedef tipoNodo *Lista;

/* Funciones con listas: */


void insertar(Lista *l, int v);
void borrar(Lista *l, int v);
int listaVacia(Lista l);
void borrarLista(Lista *l);
void mostrarLista(Lista l);

int main()
{

Lista lista = NULL;


pNodo p;

insertar(&lista, 20);
mostrarLista(lista);
insertar(&lista, 10);
mostrarLista(lista);
insertar(&lista, 40);
mostrarLista(lista);

20
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

insertar(&lista, 30);
mostrarLista(lista);

puts("\nLista completa:");
mostrarLista(lista);

puts("\nBorramos elemento a elemento");


borrar(&lista, 10);
mostrarLista(lista);
borrar(&lista, 15); // este elemento no existe
mostrarLista(lista);
borrar(&lista, 45);//no existe
mostrarLista(lista);
borrar(&lista, 30);
mostrarLista(lista);
borrar(&lista, 40);
mostrarLista(lista);
//borrar(&lista, 20);
borrarLista(&lista);
mostrarLista(lista);

system("PAUSE");
return 0;
}

void insertar(Lista *lista, int v)


{

pNodo nuevo, anterior;

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;

/* Si la lista est vaca */


if(listaVacia(*lista) || (*lista)->valor > v)
{
/* Aadimos la lista a continuacin del nuevo nodo */
nuevo->siguiente = *lista;
/* Ahora, el comienzo de nuestra lista es en nuevo nodo */
*lista = nuevo;

21
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

}
else
{
/* Buscar el nodo de valor menor a v */
anterior = *lista;
/* Avanzamos hasta el ltimo elemento o hasta que el siguiente tenga
un valor mayor que v */

while(anterior->siguiente && anterior->siguiente->valor <= v)


anterior = anterior->siguiente;

/* Insertamos el nuevo nodo despus del nodo anterior */


nuevo->siguiente = anterior->siguiente;
anterior->siguiente = nuevo;
}
}

void borrar(Lista *lista, int v)


{

pNodo anterior, nodo;

nodo = *lista;
anterior = NULL;
while(nodo && nodo->valor < v)
{
anterior = nodo;
nodo = nodo->siguiente;
}
if(!nodo || nodo->valor != v)
return;
else
{ /* Borrar el nodo */
if(!anterior) /* Primer elemento */
*lista = nodo->siguiente;
else /* un elemento cualquiera */
anterior->siguiente = nodo->siguiente;
free(nodo);
}
}

22
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

int listaVacia(Lista lista)


{

return (lista == NULL);


}

void borrarLista(Lista *lista)


{

pNodo nodo;

while(*lista)
{
nodo = *lista;
*lista = nodo->siguiente;
free(nodo);
}
}

void mostrarLista(Lista lista)


{

pNodo nodo = lista;

if(listaVacia(lista))
printf("Lista vacia\n");
else
{
while(nodo)
{
printf("%d -> ", nodo->valor);
nodo = nodo->siguiente;
}
printf("\n");
}
}

23
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Pilas:
Definicin de pila:

Una pila es un tipo especial de lista abierta en la que slo se pueden insertar y eliminar nodos en uno
de los extremos de la lista. Estas operaciones se conocen como "push" y "pop", respectivamente
"empujar" y "tirar". Adems, las escrituras de datos siempre son inserciones de nodos, y las lecturas
siempre eliminan el nodo ledo.
Estas caractersticas implican un comportamiento de lista LIFO (Last In First Out), el ltimo en entrar
es el primero en salir.
El smil del que deriva el nombre de la estructura es una pila de platos. Slo es posible aadir platos en
la parte superior de la pila, y slo pueden tomarse del mismo extremo.
Los tipos que definiremos normalmente para manejar pilas sern casi los mismos que para manejar
listas, tan slo cambiaremos algunos nombres:

typedef struct _nodo {


int dato;
struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Pila;

tipoNodo es el tipo para declarar nodos, evidentemente.


pNodo es el tipo para declarar punteros a un nodo.
Pila es el tipo para declarar pilas.

Es evidente, que una pila es una lista abierta. As que sigue siendo muy importante que nuestro programa
nunca pierda el valor del puntero al primer elemento, igual que pasa con las listas abiertas.
Teniendo en cuenta que las inserciones y borrados en una pila se hacen siempre en un extremo, lo que
consideramos como el primer elemento de la lista es en realidad el ltimo elemento de la pila.

Operaciones bsicas con pilas:

Las pilas tienen un conjunto de operaciones muy limitado, slo permiten las operaciones de "push" y
"pop":
Push: Aadir un elemento al final de la pila.
Pop: Leer y eliminar un elemento del final de la pila.

24
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Las operaciones con pilas son muy simples, no hay casos especiales, salvo que la pila est vaca.

Push en una pila vaca:

Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a l, adems el
puntero a la pila valdr NULL:

El proceso es muy simple, bastar con que:


nodo->siguiente apunte a NULL.
Pila apunte a nodo.

Push en una pila no vaca:

Podemos considerar el caso anterior como un caso particular de ste, la nica diferencia es que podemos
y debemos trabajar con una pila vaca como con una pila normal.
De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l, y de una pila, en este caso
no vaca:

El proceso sigue siendo muy sencillo:


Hacemos que nodo->siguiente apunte a Pila.
Hacemos que Pila apunte a nodo.

Pop, leer y eliminar un elemento:

Ahora slo existe un caso posible, ya que slo podemos leer desde un extremo de la pila.
Partiremos de una pila con uno o ms nodos, y usaremos un puntero auxiliar, nodo:

Hacemos que nodo apunte al primer elemento de la pila, es decir a Pila.


Asignamos a Pila la direccin del segundo nodo de la pila: Pila->siguiente.
Guardamos el contenido del nodo para devolverlo como retorno de la funcin (recordemos que la
operacin pop equivale a leer y borrar.)
Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

Si la pila slo tiene un nodo, el proceso sigue siendo vlido, ya que el valor de Pila->siguiente es NULL,
y despus de eliminar el ltimo nodo la pila quedar vaca, y el valor de Pila ser NULL.

25
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Ejemplo de pila en C:

Supongamos que queremos construir una pila para almacenar nmeros enteros. Haremos pruebas
intercalando varios "push" y "pop", y comprobando el resultado.

Algoritmo de la funcin "push":

Creamos un nodo para el valor que colocaremos en la pila.


Hacemos que nodo->siguiente apunte a Pila.
Hacemos que Pila apunte a nodo.

void Push(Pila *pila, int v)


{
pNodo nuevo;
/* Crear un nodo nuevo */
nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;

/* Aadimos la pila a continuacin del nuevo nodo */


nuevo->siguiente = *pila;
/* Ahora, el comienzo de nuestra pila es en nuevo nodo */
*pila = nuevo;
}

Algoritmo de la funcin "pop":

Hacemos que nodo apunte al primer elemento de la pila, es decir a Pila.


Asignamos a Pila la direccin del segundo nodo de la pila: Pila->siguiente.

Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin pop equivale
a leer y borrar.

Liberamos la memoria asignada al primer nodo, el que queremos eliminar.


int Pop(Pila *pila)
{
pNodo nodo; /* variable auxiliar para manipular nodo */
int v; /* variable auxiliar para retorno */

/* Nodo apunta al primer elemento de la pila */


nodo = *pila;
if(!nodo)
return 0; /* Si no hay nodos en la pila retornamos 0 */

/* Asignamos a pila toda la pila menos el primer elemento */


*pila = nodo->siguiente;

/* Guardamos el valor de retorno */


v = nodo->valor;

/* Borrar el nodo */
26
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

free(nodo);
return v;
}

Cdigo completo del ejemplo:

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

typedef struct _nodo {


int valor;
struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Pila;

/* Funciones con pilas: */


void Push(Pila *l, int v);
int Pop(Pila *l);

int main()
{
Pila pila = NULL;

Push(&pila, 20);
Push(&pila, 10);
printf("%d, ", Pop(&pila));
Push(&pila, 40);
Push(&pila, 30);

printf("%d, ", Pop(&pila));


printf("%d, ", Pop(&pila));
Push(&pila, 90);
printf("%d, ", Pop(&pila));
printf("%d\n", Pop(&pila));

system("PAUSE");
return 0;
}

void Push(Pila *pila, int v)


{
pNodo nuevo;

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;

/* Aadimos la pila a continuacin del nuevo nodo */


nuevo->siguiente = *pila;
/* Ahora, el comienzo de nuestra pila es en nuevo nodo */
*pila = nuevo;
}

int Pop(Pila *pila)

27
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

pNodo nodo; /* variable auxiliar para manipular nodo */


int v; /* variable auxiliar para retorno */

/* Nodo apunta al primer elemento de la pila */


nodo = *pila;
if(!nodo) return 0; /* Si no hay nodos en la pila retornamos 0 */
/* Asignamos a pila toda la pila menos el primer elemento */
*pila = nodo->siguiente;
/* Guardamos el valor de retorno */
v = nodo->valor;
/* Borrar el nodo */
free(nodo);
return v;
}

Ejemplo 2 de Pilas:

Se trata de un programa que simula una calculadora que realiza las opeaciones +, -, * y /, empleando
la notacin polaca inversa: pimero se introducen los operandos (nmeros) y despus el operador que
indica la operacin a realizar:

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

typedef struct datos nodo;


struct datos /* estructura de un elemento de la pila */
{
double dato;
nodo *siguiente;
};

/* Prototipos de funciones */
void push(nodo **p, double x); // aadir un dato a la pila
double pop(nodo **p); // sacar un dato de la pila
void error(void);
nodo *nuevo_elemento(void); //reserva dinmica de memoria

int main()
{
nodo *q,*cima = NULL;
double a, b;
char op[81];

printf("Calculadora con las operaciones: + - ^ /\n");


printf("Los datos sern introducidos de la forma:\n");
printf(">operando 1\n");
printf(">operando 2\n");
printf("operador\n\n");
printf("Para salir pulse q\n\n");
do
{
printf("> ");
gets(op); /* leer un operando o un operador */

28
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

switch (*op)
{
case '+' :
b = pop(&cima);
a = pop(&cima);
printf("%g\n", a + b);
push(&cima, a+b);
break;

case '-' :
b = pop(&cima);
a = pop(&cima);
printf("%g\n", a - b);
push( &cima, a-b);
break;
case '*':
b = pop(&cima);
a = pop(&cima);
printf("%g\n", a * b);
push( &cima, a*b);
break;

case '/':
b = pop(&cima);
a = pop(&cima);
if(b==0)
{
printf("Division por CERO");
break;
}
printf("%g\n", a / b);
push( &cima, a/b);
break;

default:
push(&cima, atof(op));
}
}while(*op != 'q');

//liberamos memoria

q = cima;

while(q != NULL)
{
cima = cima->siguiente;
free (q);
q = cima;
}
} //fin de main()

/*Aadir un dato a la pila*/


void push(nodo **p, double x)
{
nodo *q, *cima;
cima = *p;
q = nuevo_elemento();

29
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

q->dato = x;
q->siguiente = cima;
cima = q;
*p=cima;
}

//recuperar de la cima

double pop(nodo **p)


{
nodo *cima;
double x;
cima= *p;
if(cima ==NULL)
{
printf("ERRRRORRRRR");
return 0;
}
else
{
x=cima->dato;
*p=cima->siguiente;
free(cima);
return x;
}
}

void error(void)
{
perror("Mem no reservada");
exit(0);
}

nodo *nuevo_elemento(void)
{
nodo *q = (nodo *)malloc(sizeof(nodo));
if(!q)
error();
return (q);
}

Colas:
Una cola es un tipo especial de lista enalazada en la que slo se pueden insertar nodos en uno de los
extremos de la lista y slo se pueden eliminar nodos en el otro. Adems, como sucede con las pilas, las
escrituras de datos siempre son inserciones de nodos, y las lecturas siempre eliminan el nodo ledo.
Este tipo de lista es conocido como lista FIFO (First In First Out), el primero en entrar es el primero en
salir.

30
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

El smil cotidiano es una cola para comprar, por ejemplo, las entradas del cine. Los nuevos compradores
slo pueden colocarse al final de la cola, y slo el primero de la cola puede comprar la entrada.
El nodo tpico para construir pilas es el mismo que vimos en los captulos anteriores para la construccin
de listas y pilas:
Los tipos que definiremos normalmente para manejar colas sern casi los mismos que para manejar listas
y pilas, tan slo cambiaremos algunos nombres:
typedef struct _nodo
{
int dato;
struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Cola;

tipoNodo es el tipo para declarar nodos, evidentemente.


pNodo es el tipo para declarar punteros a un nodo.
Cola es el tipo para declarar colas.

Es evidente, que una cola es una lista abierta. As que sigue siendo muy importante que nuestro
programa nunca pierda el valor del puntero al primer elemento, igual que pasa con las listas abiertas.
Adems, debido al funcionamiento de las colas, tambin deberemos mantener un puntero para el ltimo
elemento de la cola, que ser el punto donde insertemos nuevos nodos.
Teniendo en cuenta que las lecturas y escrituras en una cola se hacen siempre en extremos distintos, lo
ms fcil ser insertar nodos por el final, a continuacin del nodo que no tiene nodo siguiente, y leerlos
desde el principio, hay que recordar que leer un nodo implica eliminarlo de la cola.

Operaciones bsicas con colas:

De nuevo nos encontramos ante una estructura con muy pocas operaciones disponibles. Las colas slo
permiten aadir y leer elementos:

Aadir: Inserta un elemento al final de la cola.


Leer: Lee y elimina un elemento del principio de la cola.

Aadir un elemento:

Las operaciones con colas son muy sencillas, prcticamente no hay casos especiales, salvo que la cola
est vaca.

31
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Aadir elemento en una cola vaca:

Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a l, adems
los punteros que definen la cola, primero y ultimo que valdrn NULL:

El proceso es muy simple, bastar con que:


nodo->siguiente apunte a NULL.
Y que los punteros primero y ultimo apunten a nodo.

Aadir elemento en una cola no vaca:

De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l, y de una cola, en este caso,
al no estar vaca, los punteros primero y ultimo no sern nulos:

El proceso sigue siendo muy sencillo:


Hacemos que nodo->siguiente apunte a NULL.
Despus que ultimo->siguiente apunte a nodo.
Y actualizamos ultimo, haciendo que apunte a nodo.

Aadir elemento en una cola, caso general:

Para generalizar el caso anterior, slo necesitamos aadir una operacin:


Hacemos que nodo->siguiente apunte a NULL.
Si ultimo no es NULL, hacemos que ultimo->siguiente apunte a nodo.
Y actualizamos ultimo, haciendo que apunte a nodo.
Si primero es NULL, significa que la cola estaba vaca, as que haremos que primero apunte tambin a
nodo.

Leer un elemento en una cola:

Recordemos que leer un elemento de una cola, implica eliminarlo.


Ahora tambin existen dos casos, que la cola tenga un solo elemento o que tenga ms de uno.
Usaremos un puntero a un nodo auxiliar:

Hacemos que nodo apunte al primer elemento de la cola, es decir a primero.


Asignamos a primero la direccin del segundo nodo de la pila: primero->siguiente.
Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin de lectura
en colas implica tambin borrar.
Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

32
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Leer un elemento en una cola con un solo elemento:

Tambin necesitamos un puntero a un nodo auxiliar:

Hacemos que nodo apunte al primer elemento de la pila, es decir a primero.


Asignamos NULL a primero, que es la direccin del segundo nodo terico de la cola:
primero->siguiente.

Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin de lectura en
colas implica tambin borrar.
Liberamos la memoria asignada al primer nodo, el que queremos eliminar.
Hacemos que ultimo apunte a NULL, ya que la lectura ha dejado la cola vaca.

Leer un elemento en una cola, caso general:

Hacemos que nodo apunte al primer elemento de la pila, es decir a primero.


Asignamos a primero la direccin del segundo nodo de la pila: primero->siguiente.
Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin de lectura en
colas implica tambin borrar.
Liberamos la memoria asignada al primer nodo, el que queremos eliminar.
Si primero es NULL, hacemos que ultimo tambin apunte a NULL, ya que la lectura ha dejado la cola
vaca.

Ejemplo de cola en C:

Construiremos una cola para almacenar nmeros enteros. Haremos pruebas insertando varios valores y
leyndolos alternativamente para comprobar el resultado.

Algoritmo de la funcin "Anadir":

Creamos un nodo para el valor que colocaremos en la cola.


Hacemos que nodo->siguiente apunte a NULL.
Si "ultimo" no es NULL, hacemos que ultimo->siguiente apunte a nodo.
Actualizamos "ultimo" haciendo que apunte a nodo.
Si "primero" es NULL, hacemos que apunte a nodo.

void Anadir(pNodo *primero, pNodo *ultimo, int v)


{
pNodo nuevo;

33
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;

/* Este ser el ltimo nodo, no debe tener siguiente */


nuevo->siguiente = NULL;

/* Si la cola no estaba vaca, aadimos el nuevo a continuacin de ultimo */


if(*ultimo)
(*ultimo)->siguiente = nuevo;
/* Ahora, el ltimo elemento de la cola es el nuevo nodo */
*ultimo = nuevo;

/*Si primero es NULL,la cola estaba vaca, ahora primero apuntar tambin al nuevo nodo */
if(!*primero)
*primero = nuevo;
}

Algoritmo de la funcin "leer":

Hacemos que nodo apunte al primer elemento de la cola, es decir a primero.


Asignamos a primero la direccin del segundo nodo de la cola: primero->siguiente.
Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin de lectura
equivale a leer y borrar.
Liberamos la memoria asignada al primer nodo, el que queremos eliminar.
Si primero es NULL, haremos que ltimo tambin apunte a NULL, ya que la cola habr quedado vaca.

int Leer(pNodo *primero, pNodo *ultimo)


{
pNodo nodo; /* variable auxiliar para manipular nodo */
int v; /* variable auxiliar para retorno */

/* Nodo apunta al primer elemento de la pila */


nodo = *primero;
if(!nodo)
return 0; /* Si no hay nodos en la pila retornamos 0 */

/* Asignamos a primero la direccin del segundo nodo */


*primero = nodo->siguiente;

/* Guardamos el valor de retorno */


v = nodo->valor;

/* Borrar el nodo */
free(nodo);

/* Si la cola qued vaca, ultimo debe ser NULL tambin*/


if(!*primero) *ultimo = NULL;
return v;
}

34
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Cdigo completo del ejemplo:

Tan slo nos queda escribir una pequea prueba para verificar el funcionamiento de las colas:

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

typedef struct _nodo {


int valor;
struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;

/* Funciones con colas: */


void Anadir(pNodo *primero, pNodo *ultimo, int v);
int Leer(pNodo *primero, pNodo *ultimo);

int main()
{
pNodo primero = NULL, ultimo = NULL;

Anadir(&primero, &ultimo, 20);


printf("Aadir(20)\n");
Anadir(&primero, &ultimo, 10);
printf("Aadir(10)\n");
printf("Leer: %d\n", Leer(&primero, &ultimo));
Anadir(&primero, &ultimo, 40);
printf("Aadir(40)\n");
Anadir(&primero, &ultimo, 30);
printf("Aadir(30)\n");
printf("Leer: %d\n", Leer(&primero, &ultimo));
printf("Leer: %d\n", Leer(&primero, &ultimo));
Anadir(&primero, &ultimo, 90);
printf("Aadir(90)\n");
printf("Leer: %d\n", Leer(&primero, &ultimo));
printf("Leer: %d\n", Leer(&primero, &ultimo));

system("PAUSE");
return 0;
}

void Anadir(pNodo *primero, pNodo *ultimo, int v)


{
pNodo nuevo;

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;
/* Este ser el ltimo nodo, no debe tener siguiente */
nuevo->siguiente = NULL;
/* Si la cola no estaba vaca, aadimos el nuevo a continuacin de ultimo */
if(*ultimo) (*ultimo)->siguiente = nuevo;
/* Ahora, el ltimo elemento de la cola es el nuevo nodo */
*ultimo = nuevo;

35
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

/* Si primero es NULL, la cola estaba vaca, ahora primero apuntar tambin al


nuevo nodo */
if(!*primero) *primero = nuevo;
}

int Leer(pNodo *primero, pNodo *ultimo)


{
pNodo nodo; /* variable auxiliar para manipular nodo */
int v; /* variable auxiliar para retorno */

/* Nodo apunta al primer elemento de la pila */


nodo = *primero;
if(!nodo) return 0; /* Si no hay nodos en la pila retornamos 0 */
/* Asignamos a primero la direccin del segundo nodo */
*primero = nodo->siguiente;
/* Guardamos el valor de retorno */
v = nodo->valor;
/* Borrar el nodo */
free(nodo);
/* Si la cola qued vaca, ultimo debe ser NULL tambin*/
if(!*primero) *ultimo = NULL;
return v;
}

Listas circulares:

Una lista circular es una lista lineal en la que el ltimo nodo a punta al primero.
Las listas circulares evitan excepciones en las operaciones que se realicen sobre ellas. No existen casos
especiales, cada nodo siempre tiene uno anterior y uno siguiente.
En algunas listas circulares se aade un nodo especial de cabecera, de ese modo se evita la nica
excepcin posible, la de que la lista est vaca.

Los tipos que definiremos normalmente para manejar listas cerradas son los mismos que para para
manejar listas abiertas:

typedef struct _nodo


{
int dato;
struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Lista;

36
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

tipoNodo es el tipo para declarar nodos, evidentemente.


pNodo es el tipo para declarar punteros a un nodo.
Lista es el tipo para declarar listas, tanto abiertas como circulares. En el caso de las circulares, apuntar a
un nodo cualquiera de la lista.

A pesar de que las listas circulares simplifiquen las operaciones sobre ellas, tambin introducen algunas
complicaciones. Por ejemplo, en un proceso de bsqueda, no es tan sencillo dar por terminada la
bsqueda cuando el elemento buscado no existe.
Por ese motivo se suele resaltar un nodo en particular, que no tiene por qu ser siempre el mismo.
Cualquier nodo puede cumplir ese propsito, y puede variar durante la ejecucin del programa.
Otra alternativa que se usa a menudo, y que simplifica en cierto modo el uso de listas circulares es crear
un nodo especial de har la funcin de nodo cabecera. De este modo, la lista nunca estar vaca, y se
eliminan casi todos los casos especiales.

Operaciones bsicas con listas circulares:

A todos los efectos, las listas circulares son como las listas abiertas en cuanto a las operaciones que se
pueden realizar sobre ellas:
a) Aadir o insertar elementos.
b) Buscar o localizar elementos.
c) Borrar elementos.
d) Moverse a travs de la lista, siguiente.

Cada una de estas operaciones podr tener varios casos especiales, por ejemplo, tendremos que tener en
cuenta cuando se inserte un nodo en una lista vaca, o cuando se elimina el nico nodo de una lista.

a) Aadir un elemento

El nico caso especial a la hora de insertar nodos en listas circulares es cuando la lista est vaca.

a1) Aadir elemento en una lista circular vaca:

Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a l, adems el
puntero que define la lista, que valdr NULL:

El proceso es muy simple, bastar con que:


lista apunta a nodo.
lista->siguiente apunte a nodo.

37
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

a2) Aadir elemento en una lista circular no vaca:

De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l, y de una lista, en este caso,
el puntero no ser nulo:

El proceso sigue siendo muy sencillo:


Hacemos que nodo->siguiente apunte a lista->siguiente.
Despus que lista->siguiente apunte a nodo.

a3) Aadir elemento en una lista circular, caso general:

Para generalizar los dos casos anteriores, slo necesitamos aadir una operacin:
Si lista est vaca hacemos que lista apunte a nodo.
Si lista no est vaca, hacemos que nodo->siguiente apunte a lista->siguiente.
Despus que lista->siguiente apunte a nodo.

b) Buscar un elemento en una lista circular:

A la hora de buscar elementos en una lista circular slo hay que tener una precaucin, es necesario
almacenar el puntero del nodo en que se empez la bsqueda, para poder detectar el caso en que no
exista el valor que se busca. Por lo dems, la bsqueda es igual que en el caso de las listas abiertas, salvo
que podemos empezar en cualquier punto de la lista.

c) Borrar un elemento de una lista circular:

Para sta operacin podemos encontrar tres casos diferentes:

Eliminar un nodo cualquiera, que no sea el apuntado por lista.


Eliminar el nodo apuntado por lista, y que no sea el nico nodo.
Eliminar el nico nodo de la lista.
En el primer caso necesitamos localizar el nodo anterior al que queremos borrar. Como el principio de
la lista puede ser cualquier nodo, haremos que sea precisamente lista quien apunte al nodo anterior al
que queremos eliminar.
Esto elimina la excepcin del segundo caso, ya que lista nunca ser el nodo a eliminar, salvo que sea el
nico nodo de la lista.
Una vez localizado el nodo anterior y apuntado por lista, hacemos que lista->siguiente apunte a
nodo->siguiente. Y a continuacin borramos nodo.

38
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

En el caso de que slo exista un nodo, ser imposible localizar el nodo anterior, as que simplemente
eliminaremos el nodo, y haremos que lista valga NULL.

c1) Eliminar un nodo en una lista circular con ms de un elemento:

Consideraremos los dos primeros casos como uno slo.

El primer paso es conseguir que lista apunte al nodo anterior al que queremos eliminar. Esto se
consigue haciendo que lista valga lista->siguiente mientras lista->siguiente sea distinto
de nodo.
Hacemos que lista->siguiente apunte a nodo->siguiente.
Eliminamos el nodo.

c2) Eliminar el nico nodo en una lista circular:

Este caso es mucho ms sencillo. Si lista es el nico nodo de una lista circular:
Borramos el nodo apuntado por lista.
Hacemos que lista = NULL

Otro algoritmo para borrar nodos:

Existe un modo alternativo de eliminar un nodo en una lista circular con ms de un nodo.
Supongamos que queremos eliminar un nodo apuntado por nodo:
Copiamos el contenido del nodo->siguiente sobre el contenido de nodo.
Hacemos que nodo->siguiente apunte a nodo->siguiente->siguiente.
Eliminamos nodo->siguiente.
Si lista es el nodo->siguiente, hacemos lista = nodo.

Este mtodo tambin funciona con listas circulares de un slo elemento, salvo que el nodo a borrar es el
nico nodo que existe, y hay que hacer que lista = NULL.

Ejemplo de lista circular en C:

Construiremos una lista cerrada para almacenar nmeros enteros. Haremos pruebas insertando varios
valores, buscndolos y eliminndolos alternativamente para comprobar el resultado.

39
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Algoritmo de la funcin "Insertar":

Si lista est vaca hacemos que lista apunte a nodo.


Si lista no est vaca, hacemos que nodo->siguiente apunte a lista->siguiente.
Despus que lista->siguiente apunte a nodo.

void Insertar(Lista *lista, int v)


{
pNodo nodo;

// Creamos un nodo para el nuvo valor a insertar


nodo = (pNodo)malloc(sizeof(tipoNodo));
nodo->valor = v;

// Si la lista est vaca, la lista ser el nuevo nodo


// Si no lo est, insertamos el nuevo nodo a continuacin del apuntado
// por lista
if(*lista == NULL)
*lista = nodo;
else nodo->siguiente = (*lista)->siguiente;
// En cualquier caso, cerramos la lista circular
(*lista)->siguiente = nodo;
}

Algoritmo de la funcin "Borrar":

Tiene la lista un nico nodo?


SI:
Borrar el nodo lista.
Hacer lista = NULL.
NO:
Hacemos lista->siguiente = nodo->siguiente.
Borramos nodo.

void Borrar(Lista *lista, int v)


{
pNodo nodo;

nodo = *lista;

// Hacer que lista apunte al nodo anterior al de valor v


do
{
if((*lista)->siguiente->valor != v)
*lista = (*lista)->siguiente;
} while((*lista)->siguiente->valor != v && *lista != nodo);
// Si existe un nodo con el valor v:
if((*lista)->siguiente->valor == v)
{
// Y si la lista slo tiene un nodo
if(*lista == (*lista)->siguiente)

40
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

{
// Borrar toda la lista
free(*lista);
*lista = NULL;
}
else
{
// Si la lista tiene ms de un nodo, borrar el nodo de valor v
nodo = (*lista)->siguiente;
(*lista)->siguiente = nodo->siguiente;
free(nodo);
}
}
}

Cdigo completo del ejemplo:

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

typedef struct _nodo {


int valor;
struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Lista;

// Funciones con listas:


void Insertar(Lista *l, int v);
void Borrar(Lista *l, int v);
void BorrarLista(Lista *);
void MostrarLista(Lista l);

int main()
{
Lista lista = NULL;
pNodo p;

Insertar(&lista, 10);
Insertar(&lista, 40);
Insertar(&lista, 30);
Insertar(&lista, 20);
Insertar(&lista, 50);

MostrarLista(lista);

Borrar(&lista, 30);
Borrar(&lista, 50);

MostrarLista(lista);

BorrarLista(&lista);
system("PAUSE");
return 0;

41
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

void Insertar(Lista *lista, int v)


{
pNodo nodo;

// Creamos un nodo para el nuvo valor a insertar


nodo = (pNodo)malloc(sizeof(tipoNodo));
nodo->valor = v;

// Si la lista est vaca, la lista ser el nuevo nodo


// Si no lo est, insertamos el nuevo nodo a continuacin del apuntado
// por lista
if(*lista == NULL) *lista = nodo;
else nodo->siguiente = (*lista)->siguiente;
// En cualquier caso, cerramos la lista circular
(*lista)->siguiente = nodo;
}

void Borrar(Lista *lista, int v)


{
pNodo nodo;

nodo = *lista;

// Hacer que lista apunte al nodo anterior al de valor v


do {
if((*lista)->siguiente->valor != v) *lista = (*lista)->siguiente;
} while((*lista)->siguiente->valor != v && *lista != nodo);
// Si existe un nodo con el valor v:
if((*lista)->siguiente->valor == v) {
// Y si la lista slo tiene un nodo
if(*lista == (*lista)->siguiente) {
// Borrar toda la lista
free(*lista);
*lista = NULL;
}
else {
// Si la lista tiene ms de un nodo, borrar el nodo de valor v
nodo = (*lista)->siguiente;
(*lista)->siguiente = nodo->siguiente;
free(nodo);
}
}
}

void BorrarLista(Lista *lista)


{
pNodo nodo;

// Mientras la lista tenga ms de un nodo


while((*lista)->siguiente != *lista) {
// Borrar el nodo siguiente al apuntado por lista
nodo = (*lista)->siguiente;
(*lista)->siguiente = nodo->siguiente;
free(nodo);
}
// Y borrar el ltimo nodo

42
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

free(*lista);
*lista = NULL;
}

void MostrarLista(Lista lista)


{
pNodo nodo = lista;

do {
printf("%d -> ", nodo->valor);
nodo = nodo->siguiente;
} while(nodo != lista);
printf("\n");
}

Listas doblemente enlazadas:


Una lista doblemente enlazada es una lista lineal en la que cada nodo tiene dos enlaces, uno al nodo
siguiente, y otro al anterior.

Las listas doblemente enlazadas no necesitan un nodo especial para acceder a ellas, pueden recorrerse en
ambos sentidos a partir de cualquier nodo, esto es porque a partir de cualquier nodo, siempre es posible
alcanzar cualquier nodo de la lista, hasta que se llega a uno de los extremos.

El nodo tpico es el mismo que para construir las listas que hemos visto, salvo que tienen otro puntero al
nodo anterior:

Para C, y basndonos en la declaracin de nodo que hemos visto ms arriba, trabajaremos con los
siguientes tipos:

typedef struct _nodo {


int dato;
struct _nodo *siguiente;
struct _nodo *anterior;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Lista;

tipoNodo es el tipo para declarar nodos, evidentemente.


pNodo es el tipo para declarar punteros a un nodo.

43
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Lista es el tipo para declarar listas abiertas doblemente enlazadas. Tambin es posible, y potencialmente
til, crear listas doblemente enlazadas y circulares.

El movimiento a travs de listas doblemente enlazadas es ms sencillo, y como veremos las operaciones
de bsqueda, insercin y borrado, tambin tienen ms ventajas.

Operaciones bsicas con listas doblemente enlazadas:

De nuevo tenemos el mismo repertorio de operaciones sobre este tipo listas:


a) Aadir o insertar elementos.
b) Buscar o localizar elementos.
c) Borrar elementos.
d) Moverse a travs de la lista, siguiente y anterior.

a) Aadir un elemento:

Vamos a intentar ver todos los casos posibles de insercin de elementos en listas doblemente enlazadas.

a1) Aadir elemento en una lista doblemente enlazada vaca:

Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a l, adems el
puntero que define la lista, que valdr NULL:
El proceso es muy simple, bastar con que:
lista apunta a nodo.
lista->siguiente y lista->anterior apunten a NULL.

a2) Insertar un elemento en la primera posicin de la lista:

Partimos de una lista no vaca. Para simplificar, consideraremos que lista apunta al primer elemento de
la lista doblemente enlazada:

El proceso es el siguiente:
nodo->siguiente debe apuntar a Lista.
nodo->anterior apuntar a Lista->anterior.
Lista->anterior debe apuntar a nodo.

Recuerda que Lista no tiene por qu apuntar a ningn miembro concreto de una lista doblemente
enlazada, cualquier miembro es igualmente vlido como referencia.

44
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

a3) Insertar un elemento en la ltima posicin de la lista:

Igual que en el caso anterior, partiremos de una lista no vaca, y de nuevo para simplificar, que Lista est
apuntando al ltimo elemento de la lista:

El proceso es el siguiente:

nodo->siguiente debe apuntar a Lista->siguiente (NULL).


Lista->siguiente debe apuntar a nodo.
nodo->anterior apuntar a Lista.

a4) Insertar un elemento a continuacin de un nodo cualquiera de una lista:

Partimos de una lista no vaca, e insertaremos un nodo a continuacin de uno nodo cualquiera que no sea
el ltimo de la lista:

El proceso sigue siendo muy sencillo:


1) Hacemos que nodo->siguiente apunte a lista->siguiente
2) Hacemos que Lista->siguiente apunte a nodo.
3) Hacemos que nodo->anterior apunte a lista.
4) Hacemos que nodo->siguiente->anterior apunte a nodo.

Lo que hemos hecho es trabajar como si tuviramos dos listas enlazadas, los dos primeros pasos
equivalen a lo que hacamos para insertar elementos en una lista abierta corriente.
Los dos siguientes pasos hacen lo mismo con la lista que enlaza los nodos en sentido contrario.
El paso 4 es el ms oscuro, quizs requiera alguna explicacin.
Supongamos que disponemos de un puntero auxiliar, "p" y que antes de empezar a insertar nodo,
hacemos que apunte al nodo que quedar a continuacin de nodo despus de insertarlo, es decir

p = Lista->siguiente

Ahora empezamos el proceso de insercin, ejecutamos los pasos 1, 2 y 3. El cuarto sera slo hacer que
p->anterior apunte a nodo. Pero nodo->siguiente ya apunta a p, as que en realidad no
necesitamos el puntero auxiliar, bastar con hacer que nodo->siguiente->anterior apunte a
nodo.

45
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

a5) Aadir elemento en una lista doblemente enlazada, caso general:

Para generalizar todos los casos anteriores, slo necesitamos aadir una operacin:
Si lista est vaca hacemos que Lista apunte a nodo. Y nodo->anterior y nodo-
>siguiente a NULL.
Si lista no est vaca, hacemos que nodo->siguiente apunte a Lista->siguiente.

Despus que Lista->siguiente apunte a nodo.


Hacemos que nodo->anterior apunte a Lista.

Si nodo->siguiente no es NULL, entonces hacemos que nodo->siguiente->anterior


apunte a nodo
.
El paso 1 es equivalente a insertar un nodo en una lista vaca.
Los pasos 2 y 3 equivalen a la insercin en una lista enlazada corriente.
Los pasos 4, 5 equivalen a insertar en una lista que recorre los nodos en sentido contrario.
Existen ms casos, las listas doblemente enlazadas son mucho ms verstiles, pero todos los casos
pueden reducirse a uno de los que hemos explicado aqu.

b) Buscar un elemento en una lista doblemente enlazada:

En muchos aspectos, una lista doblemente enlazada se comporta como dos listas abiertas que comparten
los datos. En ese sentido, todo lo dicho en el captulo sobre la localizacin en listas enlazadas se puede
aplicar a listas doblemente enlazadas.
Pero adems tenemos la ventaja de que podemos avanzar y retroceder desde cualquier nodo, sin
necesidad de volver a uno de los extremos de la lista.
Por supuesto, se pueden hacer listas doblemente enlazadas no ordenadas, existen cientos de problemas
que pueden requerir de este tipo de estructuras. Pero parece que la aplicacin ms sencilla de listas
doblemente enlazadas es hacer arrays dinmicos ordenados, donde buscar un elemento concreto a partir
de cualquier otro es ms sencillo que en una lista abierta corriente.
Para recorrer una lista procederemos de un modo parecido al que usbamos con las listas abiertas, ahora
no necesitamos un puntero auxiliar, pero tenemos que tener en cuenta que Lista no tiene por qu estar en
uno de los extremos:

Retrocedemos hasta el comienzo de la lista, asignamos a lista el valor de lista->anterior mientras


lista->anterior no sea NULL.
Abriremos un bucle que al menos debe tener una condicin, que el ndice no sea NULL.
Dentro del bucle asignaremos a lista el valor del nodo siguiente al actual.
Por ejemplo, para mostrar todos los valores de los nodos de una lista, podemos usar el siguiente bucle en
C:

46
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

typedef struct _nodo {


int dato;
struct _nodo *siguiente;
struct _nodo *anterior;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Lista;
...
pNodo = indice;
...
indice = Lista;
while(indice->anterior) indice = indice->anterior;
while(indice) {
printf("%d\n", indice->dato);
indice = indice->siguiente;
}
...

Es importante que no perdamos el nodo Lista, si por error le asignramos un valor de un puntero a un
nodo que no est en la lista, no podramos acceder de nuevo a ella.
Es por eso que tendremos especial cuidado en no asignar el valor NULL a Lista.

b)Eliminar un nodo de una lista doblemente enlazada:

Analizaremos casos diferentes:


c1) Eliminar el nico nodo de una lista doblemente enlazada.
c2) Eliminar el primer nodo.
c3) Eliminar el ltimo nodo.
c4) Eliminar un nodo intermedio.

Para los casos que lo permitan consideraremos dos casos: que el nodo a eliminar es el actualmente
apuntado por Lista o que no.

c1) Eliminar el nico nodo en una lista doblemente enlazada:

En este caso, ese nodo ser el apuntado por Lista.


Eliminamos el nodo.
Hacemos que Lista apunte a NULL.

c2) Eliminar el primer nodo de una lista doblemente enlazada:

Tenemos los dos casos posibles, que el nodo a borrar est apuntado por Lista o que no. Si lo est,
simplemente hacemos que Lista sea Lista->siguiente.

47
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Si nodo apunta a Lista, hacemos que Lista apunte a Lista->siguiente.


Hacemos que nodo->siguiente->anterior apunte a NULL
Borramos el nodo apuntado por nodo.

El paso 2 separa el nodo a borrar del resto de la lista, independientemente del nodo al que apunte Lista.

c3) Eliminar el ltimo nodo de una lista doblemente enlazada:

De nuevo tenemos los dos casos posibles, que el nodo a borrar est apuntado por Lista o que no. Si lo
est, simplemente hacemos que Lista sea Lista->anterior.

Si nodo apunta a Lista, hacemos que Lista apunte a Lista->anterior.


Hacemos que nodo->anterior->siguiente apunte a NULL
Borramos el nodo apuntado por nodo.

El paso 2 depara el nodo a borrar del resto de la lista, independientemente del nodo al que apunte Lista.

c4) Eliminar un nodo intermedio de una lista doblemente enlazada:

De nuevo tenemos los dos casos posibles, que el nodo a borrar est apuntado por Lista o que no. Si lo
est, simplemente hacemos que Lista sea Lista->anterior o Lista->siguiente
Se trata de un caso ms general de los dos casos anteriores..

Si nodo apunta a Lista, hacemos que Lista apunte a Lista->anterior (o Lista->siguiente).


Hacemos que nodo->anterior->siguiente apunte a nodo->siguiente.
Hacemos que nodo->siguiente->anterior apunte a nodo->anterior.
Borramos el nodo apuntado por nodo.

c5) Eliminar un nodo de una lista doblemente enlazada, caso general:

De nuevo tenemos los dos casos posibles, que el nodo a borrar est apuntado por Lista o que no. Si lo
est, simplemente hacemos que Lista sea Lista->anterior, si no es NULL o Lista->siguiente en caso
contrario.
Si nodo apunta a Lista,
Si Lista->anterior no es NULL hacemos que Lista apunte a Lista->anterior.
Si Lista->siguiente no es NULL hacemos que Lista apunte a Lista->siguiente.
Si ambos son NULL, hacemos que Lista sea NULL.
Si nodo->anterior no es NULL, hacemos que nodo->anterior->siguiente apunte a
nodo->siguiente.
48
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Si nodo->siguiente no es NULL, hacemos que nodo->siguiente->anterior apunte a


nodo->anterior.
Borramos el nodo apuntado por nodo.

Ejemplo de lista doblemente enlazada en C:

Como en el caso de los ejemplos anteriores, construiremos una lista doblemente enlazada para almacenar
nmeros enteros. Para aprovechar mejor las posibilidades de estas listas, haremos que la lista est
ordenada. Haremos pruebas insertando varios valores, buscndolos y eliminndolos alternativamente
para comprobar el resultado.

Algoritmo de insercin:

El primer paso es crear un nodo para el dato que vamos a insertar.

Si Lista est vaca, o el valor del primer elemento de la lista es mayor que el del nuevo, insertaremos el
nuevo nodo en la primera posicin de la lista.

En caso contrario, buscaremos el lugar adecuado para la insercin, tenemos un puntero "anterior". Lo
inicializamos con el valor de Lista, y avanzaremos mientras anterior->siguiente no sea NULL y el dato
que contiene anterior->siguiente sea menor o igual que el dato que queremos insertar.

Ahora ya tenemos anterior sealando al nodo adecuado, as que insertamos el nuevo nodo a continuacin
de l.

void Insertar(Lista *lista, int v)


{
pNodo nuevo, actual;

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));
nuevo->valor = v;
/* Colocamos actual en la primera posicin de la lista */
actual = *lista;
if(actual) while(actual->anterior) actual = actual->anterior;

/* Si la lista est vaca o el primer miembro es mayor que el nuevo */


if(!actual || actual->valor > v) {
/* Aadimos la lista a continuacin del nuevo nodo */
nuevo->siguiente = actual;
nuevo->anterior = NULL;
if(actual) actual->anterior = nuevo;
if(!*lista) *lista = nuevo;
}
else {
/* Avanzamos hasta el ltimo elemento o hasta que el siguiente tenga
un valor mayor que v */
while(actual->siguiente &&actual->siguiente->valor <= v)

49
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

actual = actual->siguiente;
/* Insertamos el nuevo nodo despus del nodo anterior */
nuevo->siguiente = actual->siguiente;
actual->siguiente = nuevo;
nuevo->anterior = actual;
if(nuevo->siguiente) nuevo->siguiente->anterior = nuevo;
}
}

Algoritmo de la funcin "Borrar":

Localizamos el nodo de valor v


Existe?
SI:
Es el nodo apuntado por lista?
SI: Hacer que lista apunte a otro sitio.
Es el primer nodo de la lista?
NO: nodo->anterior->siguiente = nodo->siguiente
Es el ltimo nodo de la lista?
NO: nodo->siguiente->anterior = nodo->anterior
Borrar nodo

void Borrar(Lista *lista, int v)


{
pNodo nodo;

/* Buscar el nodo de valor v */


nodo = *lista;
while(nodo && nodo->valor <v) nodo = nodo->siguiente;
while(nodo && nodo->valor > v) nodo = nodo->anterior;

/* El valor v no est en la lista */


if(!nodo || nodo->valor != v) return;

/* Borrar el nodo */
/* Si lista apunta al nodo que queremos borrar, apuntar a otro */
if(nodo == *lista)
if(nodo->anterior) *lista = nodo->anterior;
else *lista = nodo->siguiente;

if(nodo->anterior) /* no es el primer elemento */


nodo->anterior->siguiente = nodo->siguiente;
if(nodo->siguiente) /* no es el ltimo nodo */
nodo->siguiente->anterior = nodo->anterior;
free(nodo);
}

50
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Cdigo del ejemplo completo:

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

#define ASCENDENTE 1
#define DESCENDENTE 0

typedef struct _nodo {


int valor;
struct _nodo *siguiente;
struct _nodo *anterior;
} tipoNodo;

typedef tipoNodo *pNodo;


typedef tipoNodo *Lista;

/* Funciones con listas: */


void Insertar(Lista *l, int v);
void Borrar(Lista *l, int v);

void BorrarLista(Lista *);


void MostrarLista(Lista l, int orden);

int main()
{
Lista lista = NULL;
pNodo p;

Insertar(&lista, 20);
Insertar(&lista, 10);
Insertar(&lista, 40);
Insertar(&lista, 30);

MostrarLista(lista, ASCENDENTE);
MostrarLista(lista, DESCENDENTE);

Borrar(&lista, 10);
Borrar(&lista, 15);
Borrar(&lista, 45);
Borrar(&lista, 30);

MostrarLista(lista, ASCENDENTE);
MostrarLista(lista, DESCENDENTE);

BorrarLista(&lista);

system("PAUSE");
return 0;
}

void Insertar(Lista *lista, int v)


{
pNodo nuevo, actual;

/* Crear un nodo nuevo */


nuevo = (pNodo)malloc(sizeof(tipoNodo));

51
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

nuevo->valor = v;

/* Colocamos actual en la primera posicin de la lista */


actual = *lista;
if(actual) while(actual->anterior) actual = actual->anterior;
/* Si la lista est vaca o el primer miembro es mayor que el nuevo */
if(!actual || actual->valor > v) {
/* Aadimos la lista a continuacin del nuevo nodo */
nuevo->siguiente = actual;
nuevo->anterior = NULL;
if(actual) actual->anterior = nuevo;
if(!*lista) *lista = nuevo;
}
else {
/* Avanzamos hasta el ltimo elemento o hasta que el siguiente tenga
un valor mayor que v */
while(actual->siguiente &&actual->siguiente->valor <= v)
actual = actual->siguiente;
/* Insertamos el nuevo nodo despus del nodo anterior */
nuevo->siguiente = actual->siguiente;
actual->siguiente = nuevo;
nuevo->anterior = actual;
if(nuevo->siguiente) nuevo->siguiente->anterior = nuevo;
}
}

void Borrar(Lista *lista, int v)


{
pNodo nodo;

/* Buscar el nodo de valor v */


nodo = *lista;
while(nodo && nodo->valor < v) nodo = nodo->siguiente;
while(nodo && nodo->valor > v) nodo = nodo->anterior;

/* El valor v no est en la lista */


if(!nodo || nodo->valor != v) return;

/* Borrar el nodo */
/* Si lista apunta al nodo que queremos borrar, apuntar a otro */
if(nodo == *lista)
if(nodo->anterior) *lista = nodo->anterior;
else *lista = nodo->siguiente;

if(nodo->anterior) /* no es el primer elemento */


nodo->anterior->siguiente = nodo->siguiente;
if(nodo->siguiente) /* no es el ltimo nodo */
nodo->siguiente->anterior = nodo->anterior;
free(nodo);
}

void BorrarLista(Lista *lista)


{
pNodo nodo, actual;

actual = *lista;
while(actual->anterior) actual = actual->anterior;

52
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

while(actual) {
nodo = actual;
actual = actual->siguiente;
free(nodo);
}
*lista = NULL;
}

void MostrarLista(Lista lista, int orden)


{
pNodo nodo = lista;
if(!lista) printf("Lista vaca");

nodo = lista;
if(orden == ASCENDENTE) {
while(nodo->anterior) nodo = nodo->anterior;
printf("Orden ascendente: ");
while(nodo) {
printf("%d -> ", nodo->valor);
nodo = nodo->siguiente;
}
}
else {
while(nodo->siguiente) nodo = nodo->siguiente;
printf("Orden descendente: ");
while(nodo) {
printf("%d -> ", nodo->valor);
nodo = nodo->anterior;
}
}
printf("\n");
}

53
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJE C

Bibliografa:

Documento Realizado por


Ismael Camarero

Es muy aconsejable el uso del texto:


Programacin en C/C++

Francisco Javier Ceballos Sierra.


Editorial: RAMA
http://programandoenc.webcindario.com

Programacin en C. Metodologa, estructura de datos y objetos.

Luis Joyanes Aguilar e Ignacio Zahonero Martnez.


Editorial: McGraw-Hill

También podría gustarte