Sie sind auf Seite 1von 89

ESTRUCTURAS DINMICAS DE DATOS

EN LENGUAJE C







Realizado por Ismael Camarero
icamarero@gmail.com

Forma parte de la web:
http://programandoenc.webcindario.com

Junio de 2005.
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com






















2
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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:

f l oat ar r ay[ 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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 electo 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 NULL
sig sig






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 enlazadas (o abiertas):

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





4
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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.

RBOLES:

Cada elemento dispone de dos o ms punteros, pero las referencias nunca son a elementos anteriores,
de modo que la estructura se ramifica y crece igual que un rbol.

ARBOLES BINARIOS:

Son rboles donde cada nodo slo puede apuntar a dos nodos.

ARBOLES BINARIOS DE BSQUEDA (ABB)

Son rboles binarios ordenados. Desde cada nodo todos los nodos de una rama sern mayores, segn la
norma que se haya seguido para ordenar el rbol, y los de la otra rama sern menores.

RBOLES AVL:

Son tambin rboles de bsqueda, pero su estructura est ms optimizada para reducir los tiempos de
bsqueda.

ARBOLES B:

Son estructuras ms complejas, aunque tambin se trata de rboles de bsqueda, estn mucho ms
optimizados que los anteriores.



5
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
TABLAS HASH:

Son estructuras auxiliares para ordenar listas.

GRAFOS:

Es el siguiente nivel de complejidad, podemos considerar estas estructuras como rboles no
jerarquizados.


DICCIONARIOS:

Estructuras dinmicas en las que existen nodos de distintos tipos, en realidad no es obligatorio que las
estructuras dinmicas estn compuestas por un nico tipo de nodo, la flexibilidad y los tipos de
estructuras slo estn limitados por tu imaginacin como programador.




Listas simplemente enlazadas

La forma ms simple de estructura dinmica es la listasimplemente 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:


6
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
st r uct nodo
{
i nt dat o;
st r uct nodo *si g;
};

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 i nt dat o

miembro st r uct nodo *si g

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:

t ypedef st r uct s
{
i nt dat o;
st r uct s *si gui ent e;
} el ement o;

el ement o *c; / / punt er o a nodo


Ahora el ement o es un tipo de dato, sinnimo de st r uct s. El miembro si g ha sido declarado
como puntero al dato st r uct s y no como puntero a tipo de dato el ement o, 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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; / / l i st a vac a

Inicialmente *si g 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 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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:

el ement o *nuevo_ el ement o( voi d)
{
el ement o *q;
q = ( el ement o *) mal l oc( si zeof ( el ement o) ) ;
i f ( ! q)
per r or ( No se ha r eser vado memor i a par a el nuevo nodo) ;
r et ur n q; / / devuel ve l a di r ecc. de memomr i a r eser vada
}

La funcin principal tomar la forma:

i nt mai n( )
{
el ement o *q; / / punt er o a un nodo
q = NULL; / / l i st a vac a

/ / Reser va di nmi ca de mem
q = nuevo_el ement o( ) ; / / q apunt a al nodo r eci n cr eado
q- >si g = NULL; / / f i n de l i st a

/ / oper aci ones. . .

/ / Li ber aci n de memor i a r eser vada
f r ee( q) ;
}

Grficamente:
q

9
NULL
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

Cada vez que deseemos crear un nuevo nodo, debemos llamar a la funcin nuevo_el ement o( ) 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_el ement o( ) 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:

q = nuevo_el ement o( ) ;
q- >si gui ent e = 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 ), y de una lista, en este
caso no vaca, apuntada por c:

c
sig
NULL
sig sig q
NULL


Hemos creado q mediante:

q = nuevo_el ement o( ) ;

10
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Apuntamos q->sig a c:

q- >si g = c;

Ahora reapuntamos c a q:
c = q;

Es fundamental no alterar el orden de las operaciones.
Grficamente, tenemos:
c
c
NULL
q
sig
NULL
sig sig






Este sistema de insercin permite generalizar un mtodo:

/ *Pr ogr ama que cr ea una l i st a l i neal y per mi t e i nser t ar dat os numr i cos ent er os en
el pr i mer nodo. Par a f i nal i zar l a i nt r oducci n de dat os, pul sar EOF ( en wi ndows CRT
+ Z; en Li nux CTRL +D) Ant es f i nal i zar i mpr i me l os dat os i nt r oduci dos, en or den
i nver so al de i nt r oducci n como consecuenci a de que si empr e i nser t amos el nuevo nodo
en l a cabecer a de l a l i st a. */
#i ncl ude <coni o. h>
#i ncl ude <st di o. h>
#i ncl ude <st dl i b. h>

st r uct dat os
{
i nt num;
st r uct dat os *si g;
};

t ypedef dat os *p;

/ / pr ot ot i pos de f unci ones a ut i l i zar .
dat os *nuevo_nodo( voi d) ;
voi d er r or ( voi d) ;
i nt mai n( )
11
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
{
i nt cont ador = 0, n = 0;
dat os *q = NULL, *c=NULL; / / Li st a vac a
pr i nt f ( " \ nDat o numer o %d: " , cont ador ++) ;
whi l e( scanf ( " %d" , &n) ! = EOF)
{
q = nuevo_nodo( ) ;
q- >num= n; / / numes un mi embr o de l a est r uct ur a
q- >si g = c;
c = q;
pr i nt f ( " \ nDat o numer o %d: " , cont ador ++) ;
} / / f i n de whi l e
do
{
pr i nt f ( " %d\ t " , c- >num) ;
}whi l e( ( c = c- >si g) ) ;
f r ee( q) ;
get ch( ) ;
}

/ / cuer po de l a f unci n par a r eser var memor i a
dat os *nuevo_nodo( voi d)
{
dat os *q = ( dat os *) mal l oc( si zeof ( dat os) ) ;
i f ( ! q)
er r or ( ) ;
r et ur n q;
}

voi d er r or ( voi d)
{
per r or ( " Memor i a no r eser vada" ) ;
get ch( ) ;
exi t ( 1) ;
}

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



12
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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:

q


p
NULL

. . .
sig sig



El proceso grfico es:

q


p

. . .
sig
sig
sig



13
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
El fragmento de cdigo en lenguaje C que explica el proceso es:
. . . .
q = nuevo_nodo( ) ;
q- >num= n; / / n es el ent er o que i nser t amos
q- >si g = p- >si g;
p- >si g = 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;
pr i nt f ( Val or a l ocal i zar ?) ;
scanf ( %d, &x) ;
whi l e( q ! = NULL && q- >num! = x)
q = q- >si g;
. . . .

El anterior razonamiento se puede emplear para visualizar todos los elementos de una lista:
. . . .
q = c;
whi l e( q ! = NULL)
{
pr i nt f ( %d\ t , q- >num) ;
q = q- >si g;
}
. . . .
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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
. . . .
q = c;
whi l e( q ! = NULL && q- >num<100)
{
pr i nt f ( %d\ t , q- >num) ;
q = q- >si g;
}
. . . .

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- >numse 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- >si gui ent e.
Liberamos la memoria asignada al primer nodo, q, el que queremos eliminar: f r ee( 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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- >si g;

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

p- >si g = q- >si g;

Eliminamos la memoria asociada al nodo que queremos eliminar.

f r ee( q) ;

Si el nodo a eliminar es el ltimo, es procedimiento es igualmente vlido, ya que p pasar a ser el
ltimo, y p- >si g 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; / / sal vamos el punt er o a l a l i st a
whi l e( q ! = NULL)
{
c = c- >si g;
f r ee( 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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- >si gui ent e;

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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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:

voi d i nser t ar ( Li st a *l i st a, i nt v)
{
pNodo nuevo, ant er i or ;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;

/ * Si l a l i st a est vac a */
i f ( l i st aVaci a( *l i st a) | | ( *l i st a) - >val or > v) {
18
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
/ * Aadi mos l a l i st a a cont i nuaci n del nuevo nodo */
nuevo- >si gui ent e = *l i st a;
/ * Ahor a, el comi enzo de nuest r a l i st a es en nuevo nodo */
*l i st a = nuevo;
}
el se {
/ * Buscar el nodo de val or menor a v */
ant er i or = *l i st a;
/ * Avanzamos hast a el l t i mo el ement o o hast a que el si gui ent e t enga
un val or mayor que v */
whi l e( ant er i or - >si gui ent e && ant er i or - >si gui ent e- >val or <= v)
ant er i or = ant er i or - >si gui ent e;
/ * I nser t amos el nuevo nodo despus del nodo ant er i or */
nuevo- >si gui ent e = ant er i or - >si gui ent e;
ant er i or - >si gui ent e = 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


voi d bor r ar ( Li st a *l i st a, i nt v)
{
pNodo ant er i or , nodo;

nodo = *l i st a;
ant er i or = NULL;
whi l e( nodo && nodo- >val or < v) {
ant er i or = nodo;
nodo = nodo- >si gui ent e;
}
19
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
i f ( ! nodo | | nodo- >val or ! = v) r et ur n;
el se { / * Bor r ar el nodo */
i f ( ! ant er i or ) / * Pr i mer el ement o */
*l i st a = nodo- >si gui ent e;
el se / * un el ement o cual qui er a */
ant er i or - >si gui ent e = nodo- >si gui ent e;
f r ee( nodo) ;
}
}


Cdigo completo del ejemplo:

#i ncl ude <st dl i b. h>
#i ncl ude <st di o. h>

st r uct nodo {
i nt val or ;
st r uct nodo *si gui ent e;
}; / / t i poNodo;

t ypedef st r uct nodo t i poNodo;
t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Li st a;

/ * Funci ones con l i st as: */
voi d i nser t ar ( Li st a *l , i nt v) ;
voi d bor r ar ( Li st a *l , i nt v) ;
i nt l i st aVaci a( Li st a l ) ;
voi d bor r ar Li st a( Li st a *) ;
voi d most r ar Li st a( Li st a l ) ;

i nt mai n( )
{
Li st a l i st a = NULL;
pNodo p;

i nser t ar ( &l i st a, 20) ;
most r ar Li st a( l i st a) ;
i nser t ar ( &l i st a, 10) ;
most r ar Li st a( l i st a) ;
i nser t ar ( &l i st a, 40) ;
most r ar Li st a( l i st a) ;
20
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
i nser t ar ( &l i st a, 30) ;
most r ar Li st a( l i st a) ;

put s( " \ nLi st a compl et a: " ) ;
most r ar Li st a( l i st a) ;

put s( " \ nBor r amos el ement o a el ement o" ) ;
bor r ar ( &l i st a, 10) ;
most r ar Li st a( l i st a) ;
bor r ar ( &l i st a, 15) ; / / est e el ement o no exi st e
most r ar Li st a( l i st a) ;
bor r ar ( &l i st a, 45) ; / / no exi st e
most r ar Li st a( l i st a) ;
bor r ar ( &l i st a, 30) ;
most r ar Li st a( l i st a) ;
bor r ar ( &l i st a, 40) ;
most r ar Li st a( l i st a) ;
/ / bor r ar ( &l i st a, 20) ;
bor r ar Li st a( &l i st a) ;
most r ar Li st a( l i st a) ;

syst em( " PAUSE" ) ;
r et ur n 0;
}

voi d i nser t ar ( Li st a *l i st a, i nt v)
{
pNodo nuevo, ant er i or ;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;

/ * Si l a l i st a est vac a */
i f ( l i st aVaci a( *l i st a) | | ( *l i st a) - >val or > v)
{
/ * Aadi mos l a l i st a a cont i nuaci n del nuevo nodo */
nuevo- >si gui ent e = *l i st a;
/ * Ahor a, el comi enzo de nuest r a l i st a es en nuevo nodo */
*l i st a = nuevo;
21
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
}
el se
{
/ * Buscar el nodo de val or menor a v */
ant er i or = *l i st a;
/ * Avanzamos hast a el l t i mo el ement o o hast a que el si gui ent e t enga
un val or mayor que v */

whi l e( ant er i or - >si gui ent e && ant er i or - >si gui ent e- >val or <= v)
ant er i or = ant er i or - >si gui ent e;

/ * I nser t amos el nuevo nodo despus del nodo ant er i or */
nuevo- >si gui ent e = ant er i or - >si gui ent e;
ant er i or - >si gui ent e = nuevo;
}
}

voi d bor r ar ( Li st a *l i st a, i nt v)
{
pNodo ant er i or , nodo;

nodo = *l i st a;
ant er i or = NULL;
whi l e( nodo && nodo- >val or < v)
{
ant er i or = nodo;
nodo = nodo- >si gui ent e;
}
i f ( ! nodo | | nodo- >val or ! = v)
r et ur n;
el se
{ / * Bor r ar el nodo */
i f ( ! ant er i or ) / * Pr i mer el ement o */
*l i st a = nodo- >si gui ent e;
el se / * un el ement o cual qui er a */
ant er i or - >si gui ent e = nodo- >si gui ent e;
f r ee( nodo) ;
}
}

22
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
i nt l i st aVaci a( Li st a l i st a)
{
r et ur n ( l i st a == NULL) ;
}

voi d bor r ar Li st a( Li st a *l i st a)
{
pNodo nodo;

whi l e( *l i st a)
{
nodo = *l i st a;
*l i st a = nodo- >si gui ent e;
f r ee( nodo) ;
}
}
voi d most r ar Li st a( Li st a l i st a)
{
pNodo nodo = l i st a;

i f ( l i st aVaci a( l i st a) )
pr i nt f ( " Li st a vaci a\ n" ) ;
el se
{
whi l e( nodo)
{
pr i nt f ( " %d - > " , nodo- >val or ) ;
nodo = nodo- >si gui ent e;
}
pr i nt f ( " \ n" ) ;
}
}






23
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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:

t ypedef st r uct _nodo {
i nt dat o;
st r uct _nodo *si gui ent e;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Pi l a;

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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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.

Ejemplo de pila en C:
25
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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.

voi d Push( Pi l a *pi l a, i nt v)
{
pNodo nuevo;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;

/ * Aadi mos l a pi l a a cont i nuaci n del nuevo nodo */
nuevo- >si gui ent e = *pi l a;
/ * Ahor a, el comi enzo de nuest r a pi l a es en nuevo nodo */
*pi l a = 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.

i nt Pop( Pi l a *pi l a)
{
pNodo nodo; / * var i abl e auxi l i ar par a mani pul ar nodo */
i nt v; / * var i abl e auxi l i ar par a r et or no */

/ * Nodo apunt a al pr i mer el ement o de l a pi l a */
nodo = *pi l a;
i f ( ! nodo)
r et ur n 0; / * Si no hay nodos en l a pi l a r et or namos 0 */

/ * Asi gnamos a pi l a t oda l a pi l a menos el pr i mer el ement o */
*pi l a = nodo- >si gui ent e;

/ * Guar damos el val or de r et or no */
v = nodo- >val or ;

/ * Bor r ar el nodo */
26
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
f r ee( nodo) ;
r et ur n v;
}

Cdigo completo del ejemplo:

#i ncl ude <st dl i b. h>
#i ncl ude <st di o. h>

t ypedef st r uct _nodo {
i nt val or ;
st r uct _nodo *si gui ent e;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Pi l a;

/ * Funci ones con pi l as: */
voi d Push( Pi l a *l , i nt v) ;
i nt Pop( Pi l a *l ) ;

i nt mai n( )
{
Pi l a pi l a = NULL;

Push( &pi l a, 20) ;
Push( &pi l a, 10) ;
pr i nt f ( " %d, " , Pop( &pi l a) ) ;
Push( &pi l a, 40) ;
Push( &pi l a, 30) ;

pr i nt f ( " %d, " , Pop( &pi l a) ) ;
pr i nt f ( " %d, " , Pop( &pi l a) ) ;
Push( &pi l a, 90) ;
pr i nt f ( " %d, " , Pop( &pi l a) ) ;
pr i nt f ( " %d\ n" , Pop( &pi l a) ) ;

syst em( " PAUSE" ) ;
r et ur n 0;
}

voi d Push( Pi l a *pi l a, i nt v)
{
pNodo nuevo;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;

/ * Aadi mos l a pi l a a cont i nuaci n del nuevo nodo */
nuevo- >si gui ent e = *pi l a;
/ * Ahor a, el comi enzo de nuest r a pi l a es en nuevo nodo */
*pi l a = nuevo;
}

i nt Pop( Pi l a *pi l a)
27
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
{
pNodo nodo; / * var i abl e auxi l i ar par a mani pul ar nodo */
i nt v; / * var i abl e auxi l i ar par a r et or no */

/ * Nodo apunt a al pr i mer el ement o de l a pi l a */
nodo = *pi l a;
i f ( ! nodo) r et ur n 0; / * Si no hay nodos en l a pi l a r et or namos 0 */
/ * Asi gnamos a pi l a t oda l a pi l a menos el pr i mer el ement o */
*pi l a = nodo- >si gui ent e;
/ * Guar damos el val or de r et or no */
v = nodo- >val or ;
/ * Bor r ar el nodo */
f r ee( nodo) ;
r et ur n 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:

#i ncl ude <st di o. h>
#i ncl ude <st dl i b. h>


t ypedef st r uct dat os nodo;
st r uct dat os / * est r uct ur a de un el ement o de l a pi l a */
{
doubl e dat o;
nodo *si gui ent e;
};

/ * Pr ot ot i pos de f unci ones */
voi d push( nodo **p, doubl e x) ; / / aadi r un dat o a l a pi l a
doubl e pop( nodo **p) ; / / sacar un dat o de l a pi l a
voi d er r or ( voi d) ;
nodo *nuevo_el ement o( voi d) ; / / r eser va di nmi ca de memor i a

i nt mai n( )
{
nodo *q, *ci ma = NULL;
doubl e a, b;
char op[ 81] ;

pr i nt f ( " Cal cul ador a con l as oper aci ones: + - ^ / \ n" ) ;
pr i nt f ( " Los dat os ser n i nt r oduci dos de l a f or ma: \ n" ) ;
pr i nt f ( " >oper ando 1\ n" ) ;
pr i nt f ( " >oper ando 2\ n" ) ;
pr i nt f ( " oper ador \ n\ n" ) ;
pr i nt f ( " Par a sal i r pul se q\ n\ n" ) ;
do
{
pr i nt f ( " > " ) ;
get s( op) ; / * l eer un oper ando o un oper ador */

28
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
swi t ch ( *op)
{
case ' +' :
b = pop( &ci ma) ;
a = pop( &ci ma) ;
pr i nt f ( " %g\ n" , a + b) ;
push( &ci ma, a+b) ;
br eak;

case ' - ' :
b = pop( &ci ma) ;
a = pop( &ci ma) ;
pr i nt f ( " %g\ n" , a - b) ;
push( &ci ma, a- b) ;
br eak;

case ' *' :
b = pop( &ci ma) ;
a = pop( &ci ma) ;
pr i nt f ( " %g\ n" , a * b) ;
push( &ci ma, a*b) ;
br eak;

case ' / ' :
b = pop( &ci ma) ;
a = pop( &ci ma) ;
i f ( b==0)
{
pr i nt f ( " Di vi si on por CERO" ) ;
br eak;
}
pr i nt f ( " %g\ n" , a / b) ;
push( &ci ma, a/ b) ;
br eak;

def aul t :
push( &ci ma, at of ( op) ) ;
}
}whi l e( *op ! = ' q' ) ;

/ / l i ber amos memor i a

q = ci ma;

whi l e( q ! = NULL)
{
ci ma = ci ma- >si gui ent e;
f r ee ( q) ;
q = ci ma;
}
} / / f i n de mai n( )

/ *Aadi r un dat o a l a pi l a*/
voi d push( nodo **p, doubl e x)
{
nodo *q, *ci ma;
ci ma = *p;
q = nuevo_el ement o( ) ;
29
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
q- >dat o = x;
q- >si gui ent e = ci ma;
ci ma = q;
*p=ci ma;
}

/ / r ecuper ar de l a ci ma

doubl e pop( nodo **p)
{
nodo *ci ma;
doubl e x;
ci ma= *p;
i f ( ci ma ==NULL)
{
pr i nt f ( " ERRRRORRRRR" ) ;
r et ur n 0;
}
el se
{
x=ci ma- >dat o;
*p=ci ma- >si gui ent e;
f r ee( ci ma) ;
r et ur n x;
}
}

voi d er r or ( voi d)
{
per r or ( " Memno r eser vada" ) ;
exi t ( 0) ;
}

nodo *nuevo_el ement o( voi d)
{
nodo *q = ( nodo *) mal l oc( si zeof ( nodo) ) ;
i f ( ! q)
er r or ( ) ;
r et ur n ( 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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:
t ypedef st r uct _nodo
{
i nt dat o;
st r uct _nodo *si gui ent e;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Col a;

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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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 pr i mer o.
Asignamos a primero la direccin del segundo nodo de la pila: pr i mer o- >si gui ent e.
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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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 pr i mer o.
Asignamos NULL a pr i mer o, que es la direccin del segundo nodo terico de la cola:
pr i mer o- >si gui ent e.

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 ul t i mo 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 pr i mer o.
Asignamos a primero la direccin del segundo nodo de la pila: pr i mer o- >si gui ent e.
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- >si gui ent e apunte a NULL.
Si "ultimo" no es NULL, hacemos que ul t i mo- >si gui ent e apunte a nodo.
Actualizamos "ultimo" haciendo que apunte a nodo.
Si "primero" es NULL, hacemos que apunte a nodo.

voi d Anadi r ( pNodo *pr i mer o, pNodo *ul t i mo, i nt v)
{
pNodo nuevo;

33
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;

/ * Est e ser el l t i mo nodo, no debe t ener si gui ent e */
nuevo- >si gui ent e = NULL;

/ * Si l a col a no est aba vac a, aadi mos el nuevo a cont i nuaci n de ul t i mo */
i f ( *ul t i mo)
( *ul t i mo) - >si gui ent e = nuevo;

/ * Ahor a, el l t i mo el ement o de l a col a es el nuevo nodo */
*ul t i mo = nuevo;

/ *Si pr i mer o es NULL, l a col a est aba vac a, ahor a pr i mer o apunt ar t ambi n al nuevo nodo */
i f ( ! *pr i mer o)
*pr i mer o = nuevo;
}


Algoritmo de la funcin "leer":

Hacemos que nodo apunte al primer elemento de la cola, es decir a pr i mer o.
Asignamos a primero la direccin del segundo nodo de la cola: pr i mer o- >si gui ent e.
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.

i nt Leer ( pNodo *pr i mer o, pNodo *ul t i mo)
{
pNodo nodo; / * var i abl e auxi l i ar par a mani pul ar nodo */
i nt v; / * var i abl e auxi l i ar par a r et or no */

/ * Nodo apunt a al pr i mer el ement o de l a pi l a */
nodo = *pr i mer o;
i f ( ! nodo)
r et ur n 0; / * Si no hay nodos en l a pi l a r et or namos 0 */

/ * Asi gnamos a pr i mer o l a di r ecci n del segundo nodo */
*pr i mer o = nodo- >si gui ent e;

/ * Guar damos el val or de r et or no */
v = nodo- >val or ;

/ * Bor r ar el nodo */
f r ee( nodo) ;

/ * Si l a col a qued vac a, ul t i mo debe ser NULL t ambi n*/
i f ( ! *pr i mer o) *ul t i mo = NULL;
r et ur n v;
}


34
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Cdigo completo del ejemplo:

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

#i ncl ude <st dl i b. h>
#i ncl ude <st di o. h>

t ypedef st r uct _nodo {
i nt val or ;
st r uct _nodo *si gui ent e;
} t i poNodo;

t ypedef t i poNodo *pNodo;

/ * Funci ones con col as: */
voi d Anadi r ( pNodo *pr i mer o, pNodo *ul t i mo, i nt v) ;
i nt Leer ( pNodo *pr i mer o, pNodo *ul t i mo) ;

i nt mai n( )
{
pNodo pr i mer o = NULL, ul t i mo = NULL;

Anadi r ( &pr i mer o, &ul t i mo, 20) ;
pr i nt f ( " Aadi r ( 20) \ n" ) ;
Anadi r ( &pr i mer o, &ul t i mo, 10) ;
pr i nt f ( " Aadi r ( 10) \ n" ) ;
pr i nt f ( " Leer : %d\ n" , Leer ( &pr i mer o, &ul t i mo) ) ;
Anadi r ( &pr i mer o, &ul t i mo, 40) ;
pr i nt f ( " Aadi r ( 40) \ n" ) ;
Anadi r ( &pr i mer o, &ul t i mo, 30) ;
pr i nt f ( " Aadi r ( 30) \ n" ) ;
pr i nt f ( " Leer : %d\ n" , Leer ( &pr i mer o, &ul t i mo) ) ;
pr i nt f ( " Leer : %d\ n" , Leer ( &pr i mer o, &ul t i mo) ) ;
Anadi r ( &pr i mer o, &ul t i mo, 90) ;
pr i nt f ( " Aadi r ( 90) \ n" ) ;
pr i nt f ( " Leer : %d\ n" , Leer ( &pr i mer o, &ul t i mo) ) ;
pr i nt f ( " Leer : %d\ n" , Leer ( &pr i mer o, &ul t i mo) ) ;

syst em( " PAUSE" ) ;
r et ur n 0;
}

voi d Anadi r ( pNodo *pr i mer o, pNodo *ul t i mo, i nt v)
{
pNodo nuevo;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;
/ * Est e ser el l t i mo nodo, no debe t ener si gui ent e */
nuevo- >si gui ent e = NULL;
/ * Si l a col a no est aba vac a, aadi mos el nuevo a cont i nuaci n de ul t i mo */
i f ( *ul t i mo) ( *ul t i mo) - >si gui ent e = nuevo;
/ * Ahor a, el l t i mo el ement o de l a col a es el nuevo nodo */
*ul t i mo = nuevo;
35
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
/ * Si pr i mer o es NULL, l a col a est aba vac a, ahor a pr i mer o apunt ar t ambi n al
nuevo nodo */
i f ( ! *pr i mer o) *pr i mer o = nuevo;
}

i nt Leer ( pNodo *pr i mer o, pNodo *ul t i mo)
{
pNodo nodo; / * var i abl e auxi l i ar par a mani pul ar nodo */
i nt v; / * var i abl e auxi l i ar par a r et or no */

/ * Nodo apunt a al pr i mer el ement o de l a pi l a */
nodo = *pr i mer o;
i f ( ! nodo) r et ur n 0; / * Si no hay nodos en l a pi l a r et or namos 0 */
/ * Asi gnamos a pr i mer o l a di r ecci n del segundo nodo */
*pr i mer o = nodo- >si gui ent e;
/ * Guar damos el val or de r et or no */
v = nodo- >val or ;
/ * Bor r ar el nodo */
f r ee( nodo) ;
/ * Si l a col a qued vac a, ul t i mo debe ser NULL t ambi n*/
i f ( ! *pr i mer o) *ul t i mo = NULL;
r et ur n v;
}






Listas circulares:


Una lista circular es unalista 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:

t ypedef st r uct _nodo
{
i nt dat o;
st r uct _nodo *si gui ent e;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Li st a;
36
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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:
l i st a apunta a nodo.
l i st a- >si gui ent e apunte a nodo.
37
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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- >si gui ent e apunte a l i st a- >si gui ent e.
Despus que l i st a- >si gui ent e 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- >si gui ent e apunte a l i st a- >si gui ent e.
Despus que l i st a- >si gui ent e 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 l i st a- >si gui ent e apunte a
nodo- >si gui ent e. Y a continuacin borramos nodo.
38
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
En el caso de que slo exista un nodo, ser imposible localizar el nodo anterior, as que simplemente
eliminaremos el nodo, y haremos que l i st a 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 l i st a apunte al nodo anterior al que queremos eliminar. Esto se
consigue haciendo que lista valga l i st a- >si gui ent e mientras l i st a- >si gui ent e sea distinto
de nodo.
Hacemos que l i st a- >si gui ent e apunte a nodo- >si gui ent e.
Eliminamos el nodo.

c2) Eliminar el nico nodo en una lista circular:

Este caso es mucho ms sencillo. Si l i st a es el nico nodo de una lista circular:
Borramos el nodo apuntado por l i st a.
Hacemos que l i st a = 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- >si gui ent e sobre el contenido de nodo.
Hacemos que nodo- >si gui ent e apunte a nodo- >si gui ent e- >si gui ent e.
Eliminamos nodo- >si gui ent e.
Si lista es el nodo- >si gui ent e, hacemos l i st a = 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 l i st a = 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Algoritmo de la funcin "Insertar":

Si lista est vaca hacemos que l i st a apunte a nodo.
Si lista no est vaca, hacemos que nodo- >si gui ent e apunte a l i st a- >si gui ent e.
Despus que l i st a- >si gui ent e apunte a nodo.


voi d I nser t ar ( Li st a *l i st a, i nt v)
{
pNodo nodo;

/ / Cr eamos un nodo par a el nuvo val or a i nser t ar
nodo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nodo- >val or = v;

/ / Si l a l i st a est vac a, l a l i st a ser el nuevo nodo
/ / Si no l o est , i nser t amos el nuevo nodo a cont i nuaci n del apunt ado
/ / por l i st a
i f ( *l i st a == NULL)
*l i st a = nodo;
el se nodo- >si gui ent e = ( *l i st a) - >si gui ent e;
/ / En cual qui er caso, cer r amos l a l i st a ci r cul ar
( *l i st a) - >si gui ent e = nodo;
}

Algoritmo de la funcin "Borrar":

Tiene la lista un nico nodo?
SI:
Borrar el nodo lista.
Hacer l i st a = NULL.
NO:
Hacemos l i st a- >si gui ent e = nodo- >si gui ent e.
Borramos nodo.

voi d Bor r ar ( Li st a *l i st a, i nt v)
{
pNodo nodo;

nodo = *l i st a;

/ / Hacer que l i st a apunt e al nodo ant er i or al de val or v
do
{
i f ( ( *l i st a) - >si gui ent e- >val or ! = v)
*l i st a = ( *l i st a) - >si gui ent e;
} whi l e( ( *l i st a) - >si gui ent e- >val or ! = v && *l i st a ! = nodo) ;
/ / Si exi st e un nodo con el val or v:
i f ( ( *l i st a) - >si gui ent e- >val or == v)
{
/ / Y si l a l i st a sl o t i ene un nodo
i f ( *l i st a == ( *l i st a) - >si gui ent e)
40
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
{
/ / Bor r ar t oda l a l i st a
f r ee( *l i st a) ;
*l i st a = NULL;
}
el se
{
/ / Si l a l i st a t i ene ms de un nodo, bor r ar el nodo de val or v
nodo = ( *l i st a) - >si gui ent e;
( *l i st a) - >si gui ent e = nodo- >si gui ent e;
f r ee( nodo) ;
}
}
}


Cdigo completo del ejemplo:

#i ncl ude <st dl i b. h>
#i ncl ude <st di o. h>

t ypedef st r uct _nodo {
i nt val or ;
st r uct _nodo *si gui ent e;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Li st a;

/ / Funci ones con l i st as:
voi d I nser t ar ( Li st a *l , i nt v) ;
voi d Bor r ar ( Li st a *l , i nt v) ;
voi d Bor r ar Li st a( Li st a *) ;
voi d Most r ar Li st a( Li st a l ) ;

i nt mai n( )
{
Li st a l i st a = NULL;
pNodo p;

I nser t ar ( &l i st a, 10) ;
I nser t ar ( &l i st a, 40) ;
I nser t ar ( &l i st a, 30) ;
I nser t ar ( &l i st a, 20) ;
I nser t ar ( &l i st a, 50) ;

Most r ar Li st a( l i st a) ;

Bor r ar ( &l i st a, 30) ;
Bor r ar ( &l i st a, 50) ;

Most r ar Li st a( l i st a) ;

Bor r ar Li st a( &l i st a) ;
syst em( " PAUSE" ) ;
r et ur n 0;
41
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
}

voi d I nser t ar ( Li st a *l i st a, i nt v)
{
pNodo nodo;

/ / Cr eamos un nodo par a el nuvo val or a i nser t ar
nodo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nodo- >val or = v;

/ / Si l a l i st a est vac a, l a l i st a ser el nuevo nodo
/ / Si no l o est , i nser t amos el nuevo nodo a cont i nuaci n del apunt ado
/ / por l i st a
i f ( *l i st a == NULL) *l i st a = nodo;
el se nodo- >si gui ent e = ( *l i st a) - >si gui ent e;
/ / En cual qui er caso, cer r amos l a l i st a ci r cul ar
( *l i st a) - >si gui ent e = nodo;
}

voi d Bor r ar ( Li st a *l i st a, i nt v)
{
pNodo nodo;

nodo = *l i st a;

/ / Hacer que l i st a apunt e al nodo ant er i or al de val or v
do {
i f ( ( *l i st a) - >si gui ent e- >val or ! = v) *l i st a = ( *l i st a) - >si gui ent e;
} whi l e( ( *l i st a) - >si gui ent e- >val or ! = v && *l i st a ! = nodo) ;
/ / Si exi st e un nodo con el val or v:
i f ( ( *l i st a) - >si gui ent e- >val or == v) {
/ / Y si l a l i st a sl o t i ene un nodo
i f ( *l i st a == ( *l i st a) - >si gui ent e) {
/ / Bor r ar t oda l a l i st a
f r ee( *l i st a) ;
*l i st a = NULL;
}
el se {
/ / Si l a l i st a t i ene ms de un nodo, bor r ar el nodo de val or v
nodo = ( *l i st a) - >si gui ent e;
( *l i st a) - >si gui ent e = nodo- >si gui ent e;
f r ee( nodo) ;
}
}
}

voi d Bor r ar Li st a( Li st a *l i st a)
{
pNodo nodo;

/ / Mi ent r as l a l i st a t enga ms de un nodo
whi l e( ( *l i st a) - >si gui ent e ! = *l i st a) {
/ / Bor r ar el nodo si gui ent e al apunt ado por l i st a
nodo = ( *l i st a) - >si gui ent e;
( *l i st a) - >si gui ent e = nodo- >si gui ent e;
f r ee( nodo) ;
}
/ / Y bor r ar el l t i mo nodo
42
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
f r ee( *l i st a) ;
*l i st a = NULL;
}

voi d Most r ar Li st a( Li st a l i st a)
{
pNodo nodo = l i st a;

do {
pr i nt f ( " %d - > " , nodo- >val or ) ;
nodo = nodo- >si gui ent e;
} whi l e( nodo ! = l i st a) ;
pr i nt f ( " \ n" ) ;
}

}





Listas doblemente enlazadas:

Una listadoblemente 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:

t ypedef st r uct _nodo {
i nt dat o;
st r uct _nodo *si gui ent e;
st r uct _nodo *ant er i or ;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Li st a;

tipoNodo es el tipo para declarar nodos, evidentemente.
pNodo es el tipo para declarar punteros a un nodo.
43
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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:
l i st a apunta a nodo.
l i st a- >si gui ent e y l i st a- >ant er i or 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- >si gui ent e debe apuntar a Li st a.
nodo- >ant er i or apuntar a Li st a- >ant er i or.
Li st a- >ant er i or 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com

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- >si gui ent e debe apuntar a Lista->siguiente (NULL).
Li st a- >si gui ent e debe apuntar a nodo.
nodo- >ant er i or apuntar a Li st a.

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- >si gui ent e apunte a l i st a- >si gui ent e
2) Hacemos que Lista->siguiente apunte a nodo.
3) Hacemos que nodo- >ant er i or apunte a l i st a.
4) Hacemos que nodo- >si gui ent e- >ant er i or 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 = Li st a- >si gui ent e

Ahora empezamos el proceso de insercin, ejecutamos los pasos 1, 2 y 3. El cuarto sera slo hacer que
p- >ant er i or apunte a nodo. Pero nodo- >si gui ent e ya apunta a p, as que en realidad no
necesitamos el puntero auxiliar, bastar con hacer que nodo- >si gui ent e- >ant er i or apunte a
nodo.


45
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
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 Li st a apunte a nodo. Y nodo- >ant er i or y nodo-
>si gui ent e a NULL.
Si lista no est vaca, hacemos que nodo- >si gui ent e apunte a Li st a- >si gui ent e.

Despus que Li st a- >si gui ent e apunte a nodo.
Hacemos que nodo- >ant er i or apunte a Li st a.

Si nodo- >si gui ent e no es NULL, entonces hacemos que nodo- >si gui ent e- >ant er i or
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 l i st a- >ant er i or mientras
l i st a- >ant er i or 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 LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
t ypedef st r uct _nodo {
i nt dat o;
st r uct _nodo *si gui ent e;
st r uct _nodo *ant er i or ;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Li st a;
. . .
pNodo = i ndi ce;
. . .
i ndi ce = Li st a;
whi l e( i ndi ce- >ant er i or ) i ndi ce = i ndi ce- >ant er i or ;
whi l e( i ndi ce) {
pr i nt f ( " %d\ n" , i ndi ce- >dat o) ;
i ndi ce = i ndi ce- >si gui ent e;
}
. . .

Es importante que no perdamos el nodo Li st a, 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 Li st a sea Li st a- >si gui ent e.

47
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Si nodo apunta a Lista, hacemos que Li st a apunte a Li st a- >si gui ent e.
Hacemos que nodo- >si gui ent e- >ant er i or 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 Li st a, hacemos que Li st a apunte a Li st a- >ant er i or .
Hacemos que nodo- >ant er i or - >si gui ent e 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 Li st a apunte a Li st a- >ant er i or (o Lista->siguiente).
Hacemos que nodo- >ant er i or - >si gui ent e apunte a nodo- >si gui ent e.
Hacemos que nodo- >si gui ent e- >ant er i or apunte a nodo- >ant er i or.
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 Li st a- >ant er i or no es NULL hacemos que Li st a apunte a Li st a- >ant er i or .
Si Li st a- >si gui ent e no es NULL hacemos que Li st a apunte a Li st a- >si gui ent e.
Si ambos son NULL, hacemos que Li st a sea NULL.
Si nodo- >ant er i or no es NULL, hacemos que nodo- >ant er i or - >si gui ent e apunte a
nodo- >si gui ent e.
48
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Si nodo- >si gui ent e no es NULL, hacemos que nodo- >si gui ent e- >ant er i or apunte a
nodo- >ant er i or .
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.

voi d I nser t ar ( Li st a *l i st a, i nt v)
{
pNodo nuevo, act ual ;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
nuevo- >val or = v;

/ * Col ocamos act ual en l a pr i mer a posi ci n de l a l i st a */
act ual = *l i st a;
i f ( act ual ) whi l e( act ual - >ant er i or ) act ual = act ual - >ant er i or ;

/ * Si l a l i st a est vac a o el pr i mer mi embr o es mayor que el nuevo */
i f ( ! act ual | | act ual - >val or > v) {
/ * Aadi mos l a l i st a a cont i nuaci n del nuevo nodo */
nuevo- >si gui ent e = act ual ;
nuevo- >ant er i or = NULL;
i f ( act ual ) act ual - >ant er i or = nuevo;
i f ( ! *l i st a) *l i st a = nuevo;
}
el se {
/ * Avanzamos hast a el l t i mo el ement o o hast a que el si gui ent e t enga
un val or mayor que v */
whi l e( act ual - >si gui ent e &&act ual - >si gui ent e- >val or <= v)
49
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
act ual = act ual - >si gui ent e;
/ * I nser t amos el nuevo nodo despus del nodo ant er i or */
nuevo- >si gui ent e = act ual - >si gui ent e;
act ual - >si gui ent e = nuevo;
nuevo- >ant er i or = act ual ;
i f ( nuevo- >si gui ent e) nuevo- >si gui ent e- >ant er i or = 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


voi d Bor r ar ( Li st a *l i st a, i nt v)
{
pNodo nodo;

/ * Buscar el nodo de val or v */
nodo = *l i st a;
whi l e( nodo && nodo- >val or <v) nodo = nodo- >si gui ent e;
whi l e( nodo && nodo- >val or > v) nodo = nodo- >ant er i or ;

/ * El val or v no est en l a l i st a */
i f ( ! nodo | | nodo- >val or ! = v) r et ur n;

/ * Bor r ar el nodo */
/ * Si l i st a apunt a al nodo que quer emos bor r ar , apunt ar a ot r o */
i f ( nodo == *l i st a)
i f ( nodo- >ant er i or ) *l i st a = nodo- >ant er i or ;
el se *l i st a = nodo- >si gui ent e;

i f ( nodo- >ant er i or ) / * no es el pr i mer el ement o */
nodo- >ant er i or - >si gui ent e = nodo- >si gui ent e;
i f ( nodo- >si gui ent e) / * no es el l t i mo nodo */
nodo- >si gui ent e- >ant er i or = nodo- >ant er i or ;
f r ee( nodo) ;
}




50
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Cdigo del ejemplo completo:

#i ncl ude <st dl i b. h>
#i ncl ude <st di o. h>

#def i ne ASCENDENTE 1
#def i ne DESCENDENTE 0

t ypedef st r uct _nodo {
i nt val or ;
st r uct _nodo *si gui ent e;
st r uct _nodo *ant er i or ;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Li st a;

/ * Funci ones con l i st as: */
voi d I nser t ar ( Li st a *l , i nt v) ;
voi d Bor r ar ( Li st a *l , i nt v) ;

voi d Bor r ar Li st a( Li st a *) ;
voi d Most r ar Li st a( Li st a l , i nt or den) ;

i nt mai n( )
{
Li st a l i st a = NULL;
pNodo p;

I nser t ar ( &l i st a, 20) ;
I nser t ar ( &l i st a, 10) ;
I nser t ar ( &l i st a, 40) ;
I nser t ar ( &l i st a, 30) ;

Most r ar Li st a( l i st a, ASCENDENTE) ;
Most r ar Li st a( l i st a, DESCENDENTE) ;

Bor r ar ( &l i st a, 10) ;
Bor r ar ( &l i st a, 15) ;
Bor r ar ( &l i st a, 45) ;
Bor r ar ( &l i st a, 30) ;

Most r ar Li st a( l i st a, ASCENDENTE) ;
Most r ar Li st a( l i st a, DESCENDENTE) ;

Bor r ar Li st a( &l i st a) ;

syst em( " PAUSE" ) ;
r et ur n 0;
}

voi d I nser t ar ( Li st a *l i st a, i nt v)
{
pNodo nuevo, act ual ;

/ * Cr ear un nodo nuevo */
nuevo = ( pNodo) mal l oc( si zeof ( t i poNodo) ) ;
51
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
nuevo- >val or = v;

/ * Col ocamos act ual en l a pr i mer a posi ci n de l a l i st a */
act ual = *l i st a;
i f ( act ual ) whi l e( act ual - >ant er i or ) act ual = act ual - >ant er i or ;
/ * Si l a l i st a est vac a o el pr i mer mi embr o es mayor que el nuevo */
i f ( ! act ual | | act ual - >val or > v) {
/ * Aadi mos l a l i st a a cont i nuaci n del nuevo nodo */
nuevo- >si gui ent e = act ual ;
nuevo- >ant er i or = NULL;
i f ( act ual ) act ual - >ant er i or = nuevo;
i f ( ! *l i st a) *l i st a = nuevo;
}
el se {
/ * Avanzamos hast a el l t i mo el ement o o hast a que el si gui ent e t enga
un val or mayor que v */
whi l e( act ual - >si gui ent e &&act ual - >si gui ent e- >val or <= v)
act ual = act ual - >si gui ent e;
/ * I nser t amos el nuevo nodo despus del nodo ant er i or */
nuevo- >si gui ent e = act ual - >si gui ent e;
act ual - >si gui ent e = nuevo;
nuevo- >ant er i or = act ual ;
i f ( nuevo- >si gui ent e) nuevo- >si gui ent e- >ant er i or = nuevo;
}
}

voi d Bor r ar ( Li st a *l i st a, i nt v)
{
pNodo nodo;

/ * Buscar el nodo de val or v */
nodo = *l i st a;
whi l e( nodo && nodo- >val or < v) nodo = nodo- >si gui ent e;
whi l e( nodo && nodo- >val or > v) nodo = nodo- >ant er i or ;

/ * El val or v no est en l a l i st a */
i f ( ! nodo | | nodo- >val or ! = v) r et ur n;

/ * Bor r ar el nodo */
/ * Si l i st a apunt a al nodo que quer emos bor r ar , apunt ar a ot r o */
i f ( nodo == *l i st a)
i f ( nodo- >ant er i or ) *l i st a = nodo- >ant er i or ;
el se *l i st a = nodo- >si gui ent e;

i f ( nodo- >ant er i or ) / * no es el pr i mer el ement o */
nodo- >ant er i or - >si gui ent e = nodo- >si gui ent e;
i f ( nodo- >si gui ent e) / * no es el l t i mo nodo */
nodo- >si gui ent e- >ant er i or = nodo- >ant er i or ;
f r ee( nodo) ;
}

voi d Bor r ar Li st a( Li st a *l i st a)
{
pNodo nodo, act ual ;

act ual = *l i st a;
whi l e( act ual - >ant er i or ) act ual = act ual - >ant er i or ;

52
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
whi l e( act ual ) {
nodo = act ual ;
act ual = act ual - >si gui ent e;
f r ee( nodo) ;
}
*l i st a = NULL;
}

voi d Most r ar Li st a( Li st a l i st a, i nt or den)
{
pNodo nodo = l i st a;

i f ( ! l i st a) pr i nt f ( " Li st a vac a" ) ;

nodo = l i st a;
i f ( or den == ASCENDENTE) {
whi l e( nodo- >ant er i or ) nodo = nodo- >ant er i or ;
pr i nt f ( " Or den ascendent e: " ) ;
whi l e( nodo) {
pr i nt f ( " %d - > " , nodo- >val or ) ;
nodo = nodo- >si gui ent e;
}
}
el se {
whi l e( nodo- >si gui ent e) nodo = nodo- >si gui ent e;
pr i nt f ( " Or den descendent e: " ) ;
whi l e( nodo) {
pr i nt f ( " %d - > " , nodo- >val or ) ;
nodo = nodo- >ant er i or ;
}
}

pr i nt f ( " \ n" ) ;
}












53
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
rboles:

Definicin:

Un rbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios nodos.
Tambin se suele dar una definicin recursiva: un rbol es una estructura en compuesta por un dato y
varios rboles.
Si un rbol no est vaco, el primer nodo recibe el nombre de raz.

Raiz
A


A B C D





I GG H






E F

Figura 1


Un nodo es considerado como padre si tiene nodos sucesores. Los nodos sucesores se llaman hijos. Por
ejemplo, el nodo B es el padre de los nodos hijos E y F. El padre de H es D. Son padres A, B, D.
Dos o ms nodos con el mismo padre, se llaman hermanos. Los nodos G, h, I son hermanos.
Un nodo sin hijos se llama hoja (nodo terminal). Los nodos C, E, F, G, H, I son hojas.
Los hijos de un nodo y los hijos de este se llaman descendientes. El padre y el abuelo de un nodo son
sus ascendientes.
Un camino es una secuencia de nodos. Cada nodo puede ser alcanzado por un nico camino que
empieza en el nodo raz. La longitud de un camino es el nmero de pasos (punteros) entre nodos. La
longitud del camino ADI es 2.
54
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
El nivel de un nodo es su distancia de la raiz. El raz es de nivel cero. Los nodos hijos del raz (B, C, D)
son denivel 1. El anterior rbol es de nivel 2.

Laaltura del rbol es el nivel ms alto aumentado en 1. El anterior es un rbol de nivel 3

Orden: es el nmero potencial de hijos que puede tener cada elemento de rbol. De este modo, diremos
que un rbol en el que cada nodo puede apuntar a otros dos es de orden dos, si puede apuntar a tres ser
de orden tres, etc.

Grado: el nmero de hijos que tiene el elemento con ms hijos dentro del rbol. En el rbol del ejemplo,
el grado es tres, ya que D tiene tres hijos, y no existen elementos con ms de tres hijos. Los rboles de
grado dos reciben el nombre de rboles binarios.


Las fechas de los grficos representan punteros y se llaman ramas.

Un rbol se divide en subrboles. Un subrbol es cualquier estructura conectada por debajo del rz.
Cada nodo de un rbol es la raz de un subrbol

Los rboles con los que trabajaremos tienen otra caracterstica importante: cada nodo slo puede ser
apuntado por otro nodo, es decir, cada nodo slo tendr un padre. Esto hace que estos rboles estn
fuertemente jerarquizados, y es lo que en realidad les da la apariencia de rboles.
Otra caracterstica que normalmente tendrn nuestros rboles es que todos los nodos contengan el mismo
nmero de punteros, es decir, usaremos la misma estructura para todos los nodos del rbol. Esto hace
que la estructura sea ms sencilla, y por lo tanto tambin los programas para trabajar con ellos.
Tampoco es necesario que todos los nodos hijos de un nodo concreto existan. Es decir, que pueden
usarse todos, algunos o ninguno de los punteros de cada nodo.
Un rbol en el que en cada nodo o bien todos o ninguno de los hijos existe, se llama rbol completo.
En una cosa, los rboles se parecen al resto de las estructuras que hemos visto: dado un nodo cualquiera
de la estructura, podemos considerarlo como una estructura independiente. Es decir, un nodo cualquiera
puede ser considerado como la raz de un rbol completo.
Existen otros conceptos que definen las caractersticas del rbol, en relacin a su tamao:
Los rboles de orden dos son bastante especiales, de hecho les dedicaremos varios captulos. Estos
rboles se conocen tambin como rboles binarios.
Frecuentemente, aunque tampoco es estrictamente necesario, para hacer ms fcil moverse a travs del
rbol, aadiremos un puntero a cada nodo que apunte al nodo padre. De este modo podremos avanzar en
direccin a la raz, y no slo hacia las hojas.
Es importante conservar siempre el nodo raz ya que es el nodo a partir del cual se desarrolla el rbol, si
perdemos este nodo, perderemos el acceso a todo el rbol.

55
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
El nodo tpico de un rbol difiere de los nodos que hemos visto hasta ahora para listas, aunque slo en el
nmero de nodos. Veamos un ejemplo de nodo para crear rboles de orden tres:

st r uct nodo {
i nt dat o;
st r uct nodo *r ama1;
st r uct nodo *r ama2;
st r uct nodo *r ama3;
};

O generalizando ms:

#def i ne ORDEN 5

st r uct nodo {
i nt dat o;
st r uct nodo *r ama[ ORDEN] ;
};


Declaraciones de tipos para manejar rboles en C:

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

t ypedef st r uct _nodo {
i nt dat o;
st r uct _nodo *r ama[ ORDEN] ;
} t i poNodo;

t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Ar bol ;

Al igual que hicimos con las listas que hemos visto hasta ahora, declaramos un tipo t i poNodo para
declarar nodos, y un tipo pNodo para es el tipo para declarar punteros a un nodo.

Ar bol es el tipo para declarar rboles de orden ORDEN.

El movimiento a travs de rboles, salvo que implementemos punteros al nodo padre, ser siempre
partiendo del nodo raz hacia un nodo hoja. Cada vez que lleguemos a un nuevo nodo podremos optar
por cualquiera de los nodos a los que apunta para avanzar al siguiente nodo.

En general, intentaremos que exista algn significado asociado a cada uno de los punteros dentro de cada
nodo, los rboles que estamos viendo son abstractos, pero las aplicaciones no tienen por qu serlo. Un
ejemplo de estructura en rbol es el sistema de directorios y ficheros de un sistema operativo. Aunque en
56
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
este caso se trata de rboles con nodos de dos tipos, nodos directotio y nodos fichero, podramos
considerar que los nodos hoja son ficheros y los nodos rama son directorios.
Otro ejemplo podra ser la tabla de contenido de un libro, por ejemplo de este mismo curso, dividido en
captulos, y cada uno de ellos en subcaptulos. Aunque el libro sea algo lineal, como una lista, en el que
cada captulo sigue al anterior, tambin es posible acceder a cualquier punto de l a travs de la tabla de
contenido.
Tambin se suelen organizar en forma de rbol los organigramas de mando en empresas o en el ejrcito,
y los rboles genealgicos.

Operaciones bsicas con rboles:

Salvo que trabajemos con rboles especiales, las inserciones sern siempre en punteros de nodos hoja o
en punteros libres de nodos rama. Con estas estructuras no es tan fcil generalizar, ya que existen
muchas variedades de rboles.
De nuevo tenemos casi el mismo repertorio de operaciones de las que disponamos con las listas:
a) Recorrer el rbol completo.
Aadir o insertar elementos.
Buscar o localizar elementos.
Borrar elementos.
Moverse a travs del rbol.
Los algoritmos de insercin y borrado dependen en gran medida del tipo de rbol que estemos
implementando, de modo que por ahora los pasaremos por alto y nos centraremos ms en el modo de
recorrer rboles.

a) Recorrer el rbol:

El modo evidente de moverse a travs de las ramas de un rbol es siguiendo los punteros, del mismo
modo en que nos movamos a travs de las listas.
Esos recorridos dependen en gran medida del tipo y propsito del rbol, pero hay ciertos recorridos que
usaremos frecuentemente. Se trata de aquellos recorridos que incluyen todo el rbol.
Hay tres formas de recorrer un rbol completo, y las tres se suelen implementar mediante recursividad.
En los tres casos se sigue siempre a partir de cada nodo todas las ramas una por una.
Supongamos que tenemos un rbol de orden tres, y queremos recorrerlo por completo.
Partiremos del nodo raz:
Recor r er Ar bol ( r ai z) ;
La funcin Recor r er Ar bol , aplicando recursividad, ser tan sencilla como invocar de nuevo a la
funcin Recor r er Ar bol para cada una de las ramas:

voi d Recor r er Ar bol ( ar bol a)
{
i f ( a == NULL) r et ur n;
57
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Recor r er Ar bol ( a- >r ama[ 0] ) ;
Recor r er Ar bol ( a- >r ama[ 1] ) ;
Recor r er Ar bol ( a- >r ama[ 2] ) ;
}

Lo que diferencia los distintos mtodos de recorrer el rbol no es el sistema de hacerlo, sino el momento
que elegimos para procesar el valor de cada nodo con relacin a los recorridos de cada una de las ramas.

Los tres tipos son:

Pre-orden:

En este tipo de recorrido, el valor del nodo se procesa antes de recorrer las ramas:



A








rama[1] rama[0]
B
C


El recorrido en PREORDEN es A B C

voi d Pr eOr den( ar bol a)
{
i f ( a == NULL) r et ur n;
Pr ocesar ( dat o) ;
Recor r er Ar bol ( a- >r ama[ 0] ) ;
Recor r er Ar bol ( a- >r ama[ 1] ) ;
}


El recorrido en preorden del arbol de la Figura 1 sera: A B E F C D G H I




58
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
In-orden:

En este tipo de recorrido, el valor del nodo se procesa despus de recorrer la primera rama y antes de
recorrer la ltima. Esto tiene ms sentido en el caso de rboles binarios, y tambin cuando existen
ORDEN-1 datos, en cuyo caso procesaremos cada dato entre el recorrido de cada dos ramas (este es el
caso de los rboles-b):

El recorrido es: B A C


rama[0]
A
rama[1]
B
C











voi d I nOr den( ar bol a)
{
i f ( a == NULL) r et ur n;
Recor r er Ar bol ( a- >r ama[ 0] ) ;
Pr ocesar ( dat o) ;
Recor r er Ar bol ( a- >r ama[ 1] ) ;
}

El recorrido en IN-ORDEN del arbol de la Figura 1 sera: E B F A C G D H I











59
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Post-orden:

En este tipo de recorrido, el valor del nodo se procesa despus de recorrer todas las ramas: B C A








B
A
Rama 1 Rama 0
C




voi d Post Or den( ar bol a)
{
i f ( a == NULL) r et ur n;
Recor r er Ar bol ( a- >r ama[ 0] ) ;
Recor r er Ar bol ( a- >r ama[ 1] ) ;
Recor r er Ar bol ( a- >r ama[ 2] ) ;
Pr ocesar ( dat o) ;
}

El recorrido en pos-orden del arbol de la Figura 1 sera: E F B C G H I D A



Eliminar nodos de un rbol:

El proceso general es muy sencillo en este caso, pero con una importante limitacin, slo podemos
borrar nodos hoja:
El proceso sera el siguiente:
Buscar el nodo padre del que queremos eliminar.
Buscar el puntero del nodo padre que apunta al nodo que queremos borrar.
Liberar el nodo.
padre->nodo[i] =NULL;.
Cuando el nodo a borrar no sea un nodo hoja, diremos que hacemos una "poda", y en ese caso
eliminaremos el rbol cuya raz es el nodo a borrar. Se trata de un procedimiento recursivo, aplicamos el
recorrido PostOrden, y el proceso ser borrar el nodo.
El procedimiento es similar al de borrado de un nodo:
60
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Buscar el nodo padre del que queremos eliminar.
Buscar el puntero del nodo padre que apunta al nodo que queremos borrar.
Podar el rbol cuyo padre es nodo.
padre->nodo[i] =NULL;.

rboles ordenados:

Un rbol ordenado, en general, es aquel a partir del cual se puede obtener una secuencia ordenada
siguiendo uno de los recorridos posibles del rbol: inorden, preorden o postorden.
En estos rboles es importante que la secuencia se mantenga ordenada aunque se aadan o se eliminen
nodos.
Existen varios tipos de rboles ordenados, que veremos a continuacin:

rboles binarios de bsqueda (ABB):

Son rboles de orden 2 que mantienen una secuencia ordenada si se recorren en inorden.

rboles AVL:

Son rboles binarios de bsqueda equilibrados, es decir, los niveles de cada rama para cualquier nodo
no difieren en ms de 1.

rboles perfectamente equilibrados:

Son rboles binarios de bsqueda en los que el nmero de nodos de cada rama para cualquier nodo no
difieren en ms de 1. Son por lo tanto rboles AVL tambin.


rboles 2-3:

Son rboles de orden 3, que contienen dos claves en cada nodo y que estn tambin equilibrados.
Tambin generan secuencias ordenadas al recorrerlos en inorden.

rboles-B:

Caso general de rboles 2-3, que para un orden M, contienen M-1 claves.

rboles binarios de Bsqueda (ABB)

Se trata de rboles de orden 2 en los que se cumple que para cada nodo, el valor de la clave de la raz del
subrbol izquierdo es menor que el valor de la clave del nodo y que el valor de la clave raz del subrbol
derecho es mayor que el valor de la clave del nodo.

61
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Operaciones en ABB

El repertorio de operaciones que se pueden realizar sobre un ABB es parecido al que realizbamos sobre
otras estructuras de datos, ms alguna otra propia de rboles:
Buscar un elemento.
Insertar un elemento.
Borrar un elemento.
Movimientos a travs del rbol:
Izquierda.
Derecha.
Raiz.
Informacin:
Comprobar si un rbol est vaco.
Calcular el nmero de nodos.
Comprobar si el nodo es hoja.
Calcular la altura de un nodo.
Calcular la altura de un rbol.

Buscar un elemento:

Partiendo siempre del nodo raz, el modo de buscar un elemento se define de forma recursiva:
Si el rbol est vaco, terminamos la bsqueda: el elemento no est en el rbol.
Si el valor del nodo raz es igual que el del elemento que buscamos, terminamos la bsqueda con xito.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho.
El valor de retorno de una funcin de bsqueda en un ABB puede ser un puntero al nodo encontrado, o
NULL, si no se ha encontrado.

Insertar un elemento:

Para insertar un elemento nos basamos en el algoritmo de bsqueda. Si el elemento est en el rbol no lo
insertaremos. Si no lo est, lo insertaremos a continuacin del ltimo nodo visitado.
Necesitamos un puntero auxiliar para conservar una referencia al padre del nodo raz actual. El valor
inicial para ese puntero es NULL.
Padre =NULL
nodo =Raiz
Bucle: mientras actual no sea un rbol vaco o hasta que se encuentre el elemento.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo: Padre=nodo, nodo=nodo->izquierdo.
62
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho: Padre=nodo, nodo=nodo->derecho.
Si nodo no es NULL, el elemento est en el rbol, por lo tanto salimos.
Si Padre es NULL, el rbol estaba vaco, por lo tanto, el nuevo rbol slo contendr el nuevo elemento,
que ser la raz del rbol.
Si el elemento es menor que el Padre, entonces insertamos el nuevo elemento como un nuevo rbol
izquierdo de Padre.
Si el elemento es mayor que el Padre, entonces insertamos el nuevo elemento como un nuevo rbol
derecho de Padre.
Este modo de actuar asegura que el rbol sigue siendo ABB.

Borrar un elemento:

Para borrar un elemento tambin nos basamos en el algoritmo de bsqueda. Si el elemento no est en el
rbol no lo podremos borrar. Si est, hay dos casos posibles:
a) Se trata de un nodo hoja: en ese caso lo borraremos directamente.

b) Se trata de un nodo rama: en ese caso no podemos eliminarlo, puesto que perderamos todos los
elementos del rbol de que el nodo actual es padre. En su lugar buscamos el nodo ms a la izquierda del
subrbol derecho, o el ms a la derecha del subrbol izquierdo e intercambiamos sus valores. A
continuacin eliminamos el nodo hoja.
Necesitamos un puntero auxiliar para conservar una referencia al padre del nodo raz actual. El valor
inicial para ese puntero es NULL.
Padre =NULL
Si el rbol est vaco: el elemento no est en el rbol, por lo tanto salimos sin eliminar ningn elemento.
(1) Si el valor del nodo raz es igual que el del elemento que buscamos, estamos ante uno de los
siguientes casos:
El nodo raz es un nodo hoja:
Si 'Padre' es NULL, el nodo raz es el nico del rbol, por lo tanto el puntero al rbol debe ser NULL.
Si raz es la rama derecha de 'Padre', hacemos que esa rama apunte a NULL.
Si raz es la rama izquierda de 'Padre', hacemos que esa rama apunte a NULL.
Eliminamos el nodo, y salimos.
El nodo no es un nodo hoja:
Buscamos el 'nodo' ms a la izquierda del rbol derecho de raz o el ms a la derecha del rbol izquierdo.
Hay que tener en cuenta que puede que slo exista uno de esos rboles. Al mismo tiempo, actualizamos
'Padre' para que apunte al padre de 'nodo'.
Intercambiamos los elementos de los nodos raz y 'nodo'.
Borramos el nodo 'nodo'. Esto significa volver a (1), ya que puede suceder que 'nodo' no sea un nodo
hoja. (Ver ejemplo 3)
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho.

63
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Ejemplo 1: Borrar un nodo hoja

Localizamos el nodo a borrar, al tiempo que mantenemos un puntero a 'Padre'.
Hacemos que el puntero de 'Padre' que apuntaba a 'nodo', ahora apunte a NULL.
Borramos el 'nodo'.

Ejemplo 2: Borrar un nodo rama con intercambio de un nodo hoja.

Localizamos el nodo a borrar ('raz').
Buscamos el nodo ms a la derecha del rbol izquierdo de 'raz', en este caso el 3, al tiempo que
mantenemos un puntero a 'Padre' a 'nodo'.
Intercambiamos los elementos 3 y 4.
Hacemos que el puntero de 'Padre' que apuntaba a 'nodo', ahora apunte a NULL.
Borramos el 'nodo'.

Ejemplo 3: Borrar un nodo rama con intercambio de un nodo rama.

Localizamos el nodo a borrar ('raz').
Buscamos el nodo ms a la izquierda del rbol derecho de 'raz', en este caso el 12, ya que el rbol
derecho no tiene nodos a su izquierda, si optamos por la rama izquierda, estaremos en un caso anlogo.
Al mismo tiempo que mantenemos un puntero a 'Padre' a 'nodo'.
Intercambiamos los elementos 6 y 12.
Ahora tenemos que repetir el bucle para el nodo 6 de nuevo, ya que no podemos eliminarlo.

Localizamos de nuevo el nodo a borrar ('raz').
Buscamos el nodo ms a la izquierda del rbol derecho de 'raz', en este caso el 16, al mismo tiempo que
mantenemos un puntero a 'Padre' a 'nodo'.
Intercambiamos los elementos 6 y 16.
Hacemos que el puntero de 'Padre' que apuntaba a 'nodo', ahora apunte a NULL.
Borramos el 'nodo'.

Este modo de actuar asegura que el rbol sigue siendo ABB.

Movimientos a travs del rbol:

No hay mucho que contar. Nuestra estructura se referenciar siempre mediante un puntero al nodo Raiz,
este puntero no debe perderse nunca.
Para movernos a travs del rbol usaremos punteros auxiliares, de modo que desde cualquier puntero los
movimientos posibles sern: moverse al nodo raz de la rama izquierda, moverse al nodo raz de la rama
derecha o moverse al nodo Raiz del rbol.


64
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Informacin sobre un rbol:

Hay varios parmetros que podemos calcular o medir dentro de un rbol. Algunos de ellos nos darn
idea de lo eficientemente que est organizado o el modo en que funciona.

Comprobar si un rbol est vaco.

Un rbol est vaco si su raz es NULL.

Calcular el nmero de nodos.

Tenemos dos opciones para hacer esto, una es llevar siempre la cuenta de nodos en el rbol al mismo
tiempo que se aaden o eliminan elementos. La otra es, sencillamente, contarlos.
Para contar los nodos podemos recurrir a cualquiera de los tres modos de recorrer el rbol: inorden,
preorden o postorden, como accin sencillamente incrementamos el contador.

Comprobar si el nodo es hoja.

Esto es muy sencillo, basta con comprobar si tanto el rbol izquierdo como el derecho estn vacos. Si
ambos lo estn, se trata de un nodo hoja.

Calcular la altura de un nodo.

No hay un modo directo de hacer esto, ya que no nos es posible recorrer el rbol en la direccin de la
raz. De modo que tendremos que recurrir a otra tcnica para calcular la altura.
Lo que haremos es buscar el elemento del nodo de que queremos averiguar la altura. Cada vez que
avancemos un nodo incrementamos la variable que contendr la altura del nodo.
Empezamos con el nodo raz apuntando a Raiz, y la 'Altura' igual a cero.
Si el valor del nodo raz es igual que el del elemento que buscamos, terminamos la bsqueda y el valor
de la altura es 'Altura'.
Incrementamos 'Altura'.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho.




65
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Calcular la altura de un rbol.

La altura del rbol es la altura del nodo de mayor altura. Para buscar este valor tendremos que recorrer
todo el rbol, de nuevo es indiferente el tipo de recorrido que hagamos, cada vez que cambiemos de
nivel incrementamos la variable que contiene la altura del nodo actual, cuando lleguemos a un nodo hoja
compararemos su altura con la variable que contiene la altura del rbol si es mayor, actualizamos la
altura del rbol.
Iniciamos un recorrido del rbol en postorden, con la variable de altura igual a cero.
Cada vez que empecemos a recorrer una nueva rama, incrementamos la altura para ese nodo.
Despus de procesar las dos ramas, verificamos si la altura del nodo es mayor que la variable que
almacena la altura actual del rbol, si es as, actualizamos esa variable.


rboles degenerados:

Los rboles binarios de bsqueda tienen un gran inconveniente. Por ejemplo, supongamos que creamos
un ABB a partir de una lista de valores ordenada:
2, 4, 5, 8, 9, 12
Difcilmente podremos llamar a la estructura resultante un rbol:

Esto es lo que llamamos un rbol binario de bsqueda degenerado.

Ms adelante veremos una nueva estructura, el rbol AVL, que resuelve este problema, generando
rboles de bsqueda equilibrados.

Ejemplo de ABB en lenguaje C:

Vamos a ver cmo implementar en C algunas de las funciones que hemos explicado para rboles ABB,
al final se incluye un ejemplo completo para rboles de enteros.

Declaracin de tipos:
Como estamos trabajando con un rbol binario, slo necesitamos una estructura para referirnos tanto a
cualquiera de los nodos como al rbol completo. Recuerda que cualquier nodo puede ser considerado
como la raz de un rbol.

t ypedef st r uct _nodo {
i nt dat o;
st r uct _nodo *der echo;
st r uct _nodo *i zqui er do;
} t i poNodo;
66
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
t ypedef t i poNodo *pNodo;
t ypedef t i poNodo *Ar bol ;


Insertar un elemento en un rbol ABB:

Disearemos una funcin que se ajuste al algoritmo:
Padre =NULL
nodo =Raiz
Bucle: mientras actual no sea un rbol vaco o hasta que se encuentre el elemento.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo: Padre=nodo, nodo=nodo->izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho: Padre=nodo, nodo=nodo->derecho.
Si nodo no es NULL, el elemento est en el rbol, por lo tanto salimos.
Si Padre es NULL, el rbol estaba vaco, por lo tanto, el nuevo rbol slo contendr el nuevo elemento,
que ser la raz del rbol.
Si el elemento es menor que el Padre, entonces insertamos el nuevo elemento como un nuevo rbol
izquierdo de Padre.
Si el elemento es mayor que el Padre, entonces insertamos el nuevo elemento como un nuevo rbol
derecho de Padre.

voi d I nser t ar ( Ar bol *a, i nt dat )
{
pNodo padr e = NULL; / * ( 1) */
pNodo act ual = *a; / * ( 2) */

whi l e( ! Vaci o( act ual ) && dat ! = act ual - >dat o) { / * ( 3) */
padr e = act ual ;
i f ( dat < act ual - >dat o) act ual = act ual - >i zqui er do; / * ( 3- a) */
el se i f ( dat > act ual - >dat o) act ual = act ual - >der echo; / * ( 3- b) */
}

i f ( ! Vaci o( act ual ) ) r et ur n; / * ( 4) */
i f ( Vaci o( padr e) ) { / * ( 5) */
*a = ( Ar bol ) mal l oc( si zeof ( t i poNodo) ) ;
( *a) - >dat o = dat ;
( *a) - >i zqui er do = ( *a) - >der echo = NULL;
}
el se i f ( dat < padr e- >dat o) { / * ( 6) */
act ual = ( Ar bol ) mal l oc( si zeof ( t i poNodo) ) ;
padr e- >i zqui er do = act ual ;
act ual - >dat o = dat ;
act ual - >i zqui er do = act ual - >der echo = NULL;
}
el se i f ( dat > padr e- >dat o) { / * ( 7) */
act ual = ( Ar bol ) mal l oc( si zeof ( t i poNodo) ) ;
padr e- >der echo = act ual ;
act ual - >dat o = dat ;
act ual - >i zqui er do = act ual - >der echo = NULL;
}
}
67
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Eliminar un elemento de un rbol ABB:

Disearemos una funcin que se ajuste al algoritmo:

Padre =NULL
Si el rbol est vaco: el elemento no est en el rbol, por lo tanto salimos sin eliminar ningn elemento.
Si el valor del nodo raz es igual que el del elemento que buscamos, estamos ante uno de los siguientes
casos:
El nodo raz es un nodo hoja:
Si 'Padre' es NULL, el nodo raz es el nico del rbol, por lo tanto el puntero al rbol debe ser NULL.
Si raz es la rama derecha de 'Padre', hacemos que esa rama apunte a NULL.
Si raz es la rama izquierda de 'Padre', hacemos que esa rama apunte a NULL.
Eliminamos el nodo, y salimos.
El nodo no es un nodo hoja:
Buscamos el 'nodo' ms a la izquierda del rbol derecho de raz o el ms a la derecha del rbol izquierdo.
Hay que tener en cuenta que puede que slo exista uno de esos rboles. Al mismo tiempo, actualizamos
'Padre' para que apunte al padre de 'nodo'.
Intercambiamos los elementos de los nodos raz y 'nodo'.
Borramos el nodo 'nodo'. Esto significa volver a (3), ya que puede suceder que 'nodo' no sea un nodo
hoja.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho.

voi d Bor r ar ( Ar bol *a, i nt dat )
{
pNodo padr e = NULL; / * ( 1) */
pNodo act ual ;
pNodo nodo;
i nt aux;

act ual = *a;
whi l e( ! Vaci o( act ual ) ) { / * Bsqueda */
i f ( dat == act ual - >dat o) {
i f ( EsHoj a( act ual ) ) {
i f ( padr e)
i f ( padr e- >der echo == act ual ) padr e- >der echo = NULL;
el se i f ( padr e- >i zqui er do == act ual ) padr e- >i zqui er do = NULL;
f r ee( act ual ) ;
act ual = NULL;
r et ur n;
}
el se {
/ * Buscar nodo */
padr e = act ual ;
i f ( act ual - >der echo) {
nodo = act ual - >der echo;
whi l e( nodo- >i zqui er do) {
padr e = nodo;
nodo = nodo- >i zqui er do;
68
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
}
}
el se {
nodo = act ual - >i zqui er do;
whi l e( nodo- >der echo) {
padr e = nodo;
nodo = nodo- >der echo;
}
}
/ * I nt er cambi o */
aux = act ual - >dat o;
act ual - >dat o = nodo- >dat o;
nodo- >dat o = aux;
act ual = nodo;
}
}
el se {
padr e = act ual ;
i f ( dat > act ual - >dat o) act ual = act ual - >der echo;
el se i f ( dat < act ual - >dat o) act ual = act ual - >i zqui er do;
}
}
}


Buscar un elemento en un rbol ABB:

Disearemos una funcin que se ajuste al algoritmo:

Si el rbol est vaco, terminamos la bsqueda: el elemento no est en el rbol.
Si el valor del nodo raz es igual que el del elemento que buscamos, terminamos la bsqueda con xito.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho.


i nt B uscar ( Ar bol a, i nt dat )
{
pNodo act ual = a;

whi l e( ! Vaci o( act ual ) ) {
i f ( dat == act ual - >dat o) r et ur n TRUE; / * dat o encont r ado ( 2) */
el se i f ( dat < act ual - >dat o) act ual = act ual - >i zqui er do; / * ( 3) */
el se i f ( dat > act ual - >dat o) act ual = act ual - >der echo; / * ( 4) */
}
r et ur n FALSE; / * No est en r bol ( 1) */
}




69
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Comprobar si el rbol est vaco:

Esta funcin es fcil de implementar:

i nt Vaci o( Ar bol r )
{
r et ur n r ==NULL;
}

Comprobar si un nodo es hoja:

Esta funcin tambin es sencilla de implementar:
i nt EsHoj a( pNodo r )
{
r et ur n ! r - >der echo && ! r - >i zqui er do;
}

Contar nmero de nodos:

Comentbamos que para contar los nodos podemos recurrir a cualquiera de los tres modos de recorrer el
rbol: inorden, preorden o postorden y como accin incrementamos el contador de nodos. Para
implementar este algoritmo recurrimos a dos funciones:

i nt Numer oNodos( Ar bol a, i nt *cont ador )
{
*cont ador = 0;

auxCont ador ( a, cont ador ) ;
r et ur n *cont ador ;
}

voi d auxCont ador ( Ar bol nodo, i nt *c)
{
( *c) ++; / * Acci n: i ncr ement ar nmer o de nodos. ( Pr eor den) */
i f ( nodo- >i zqui er do) auxCont ador ( nodo- >i zqui er do, c) ; / * Rama i zqui er da */
i f ( nodo- >der echo) auxCont ador ( nodo- >der echo, c) ; / * Rama der echa */
}


Calcular la altura de un rbol:

Es un problema parecido al anterior, pero ahora tenemos que contar la altura, no en nmero de nodos.
Cada vez que lleguemos a un nodo hoja, verificamos si la altura del nodo es la mxima, y si lo es,
actualizamos la altura del rbol a ese valor:
Iniciamos un recorrido del rbol en postorden, con la variable de altura igual a cero.
Cada vez que empecemos a recorrer una nueva rama, incrementamos la altura para ese nodo.
70
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Despus de procesar las dos ramas, verificamos si la altura del nodo es mayor que la variable que
almacena la altura actual del rbol, si es as, actualizamos esa variable.


i nt Al t ur aAr bol ( Ar bol a, i nt *al t ur a)
{
*al t ur a = 0; / * ( 1) */

auxAl t ur a( a, 0, al t ur a) ;
r et ur n *al t ur a;
}

voi d auxAl t ur a( pNodo nodo, i nt a, i nt *al t ur a)
{
/ * ( 2) Cada vez que l l amamos a auxAl t ur a pasamos como par met r o a+1 */
i f ( nodo- >i zqui er do) auxAl t ur a( nodo- >i zqui er do, a+1, al t ur a) ; / * Rama i zqui er da */
i f ( nodo- >der echo) auxAl t ur a( nodo- >der echo, a+1, al t ur a) ; / * Rama der echa */
i f ( EsHoj a( nodo) && a > *al t ur a) *al t ur a = a; / * Pr oceso ( Post or den) ( 3) */
}


Calcular la altura del nodo que contiene un dato concreto:

Lo que haremos ser buscar el elemento del nodo del que queremos averiguar la altura. Cada vez que
avancemos un nodo incrementamos la variable que contendr la altura del nodo.
Empezamos con el nodo raz apuntando a Raiz, y la 'Altura' igual a cero.
Si el valor del nodo raz es igual que el del elemento que buscamos, terminamos la bsqueda y el valor
de la altura es 'Altura'.
Incrementamos 'Altura'.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda en el rbol
izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda en el rbol
derecho.


i nt A l t ur a( Ar bol a, i nt dat )
{
i nt al t ur a = 0;
pNodo act ual = a; / * ( 1) */

whi l e( ! Vaci o( act ual ) ) {
i f ( dat == act ual - >dat o) r et ur n al t ur a; / * dat o encont r ado. ( 2) */
el se {
al t ur a++; / * ( 3) */
i f ( dat < act ual - >dat o) act ual = act ual - >i zqui er do; / * ( 4) */
el se i f ( dat > act ual - >dat o) act ual = act ual - >der echo; / * ( 5) */
}
}
r et ur n - 1; / * No est en r bol */
}


71
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Aplicar una funcin a cada elemento del rbol, segn los tres posibles recorridos:

Todos los recorridos se aplican de forma recursiva. En este ejemplo crearemos tres funciones, una por
cada tipo de recorrido, que aplicarn una funcin al elemento de cada nodo.
La funcin a aplicar puede ser cualquiera que admita como parmetro un puntero a un entero, y que no
tenga valor de retorno.

InOrden:

En Inorden, primero procesamos el subrbol izquierdo, despus el nodo actual, y finalmente el subrbol
derecho:

voi d I nOr den( Ar bol a, voi d ( *f unc) ( i nt *) )
{
i f ( a- >i zqui er do) I nOr den( a- >i zqui er do, f unc) ; / * Subr bol i zqui er do */
f unc( &( a- >dat o) ) ; / * Apl i car l a f unci n al dat o del nodo act ual */
i f ( a- >der echo) I nOr den( a- >der echo, f unc) ; / * Subr bol der echo */
}


PreOrden:

En Preorden, primero procesamos el nodo actual, despus el subrbol izquierdo, y finalmente el subrbol
derecho:

voi d Pr eOr den( Ar bol a, voi d ( *f unc) ( i nt *) )
{
f unc( &( a- >dat o) ) ; / * Apl i car l a f unci n al dat o del nodo act ual */
i f ( a- >i zqui er do) Pr eOr den( a- >i zqui er do, f unc) ; / * Subr bol i zqui er do */
i f ( a- >der echo) Pr eOr den( a- >der echo, f unc) ; / * Subr bol der echo */
}


PostOrden:

En Postorden, primero procesamos el subrbol izquierdo, despus el subrbol derecho, y finalmente el
nodo actual:

voi d Post Or den( Ar bol a, voi d ( *f unc) ( i nt *) )
{
i f ( a- >i zqui er do) Post Or den( a- >i zqui er do, f unc) ; / * Subr bol i zqui er do */
i f ( a- >der echo) Post Or den( a- >der echo, f unc) ; / * Subr bol der echo */
f unc( &( a- >dat o) ) ; / * Apl i car l a f unci n al dat o del nodo act ual */
}

72
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Ejemplo de ABB en Lenguaje C++:

Haremos ahora lo mismo que en el ejemplo en C, pero incluyendo todas las funciones y datos en una
nica clase.

Declaracin de clase ArbolABB:

Declaramos dos clases, una para nodo y otra para ArbolABB, la clase nodo la declararemos como parte
de la clase ArbolABB, de modo que no tendremos que definir relaciones de amistad, y evitamos que
otras clases o funciones tengan acceso a los datos internos de nodo.

cl ass Ar bol ABB {
pr i vat e:
/ / / / Cl ase l ocal de Li st a par a Nodo de Ar bol Bi nar i o:
cl ass Nodo {
publ i c:
/ / Const r uct or :
Nodo( const i nt dat , Nodo *i zq=NULL, Nodo *der =NULL) :
dat o( dat ) , i zqui er do( i zq) , der echo( der ) {}
/ / Mi embr os:
i nt dat o;
Nodo *i zqui er do;
Nodo *der echo;
};

/ / Punt er os de l a l i st a, par a cabeza y nodo act ual :
Nodo *r a z;
Nodo *act ual ;
i nt cont ador ;
i nt al t ur a;

publ i c:
/ / Const r uct or y dest r uct or bsi cos:
Ar bol ABB( ) : r a z( NULL) , act ual ( NULL) {}
~Ar bol ABB( ) { Podar ( r a z) ; }
/ / I nser t ar en r bol or denado:
voi d I nser t ar ( const i nt dat ) ;
/ / Bor r ar un el ement o del r bol :
voi d Bor r ar ( const i nt dat ) ;
/ / Funci n de bsqueda:
bool Buscar ( const i nt dat ) ;
/ / Compr obar si el r bol est vac o:
bool Vaci o( Nodo *r ) { r et ur n r ==NULL; }
/ / Compr obar si es un nodo hoj a:
bool EsHoj a( Nodo *r ) { r et ur n ! r - >der echo && ! r - >i zqui er do; }
/ / Cont ar nmer o de nodos:
const i nt Numer oNodos( ) ;
const i nt Al t ur aAr bol ( ) ;
/ / Cal cul ar al t ur a de un i nt :
i nt Al t ur a( const i nt dat ) ;
/ / Devol ver r ef er enci a al i nt del nodo act ual :
i nt &Val or Act ual ( ) { r et ur n act ual - >dat o; }
73
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
/ / Mover se al nodo r a z:
voi d Rai z( ) { act ual = r a z; }
/ / Apl i car una f unci n a cada el ement o del r bol :
voi d I nOr den( voi d ( *f unc) ( i nt &) , Nodo *nodo=NULL, bool r =t r ue) ;
voi d Pr eOr den( voi d ( *f unc) ( i nt &) , Nodo *nodo=NULL, bool r =t r ue) ;
voi d Post Or den( voi d ( *f unc) ( i nt &) , Nodo *nodo=NULL, bool r =t r ue) ;
pr i vat e:
/ / Funci ones auxi l i ar es
voi d Podar ( Nodo* &) ;
voi d auxCont ador ( Nodo*) ;
voi d auxAl t ur a( Nodo*, i nt ) ;
};


Definicin de las funciones miembro:

Las definiciones de las funciones miembro de la clase no difieren demasiado de las que creamos en C.
Tan solo se han sustituido algunos punteros por referencias, y se usa el tipo bool cuando es aconsejable.
Por ejemplo, en las funciones de recorrido de rboles, la funcin invocada acepta ahora una referencia a
un entero, en lugar de un puntero a un entero.

Ejemplo de ABB en C++ empleando plantillas:

Slo nos queda generalizar la clase del ejemplo anterior implementndola en forma de plantilla.
El proceso es relativamente sencillo, slo tenemos que cambiar la declaracin de dato, y todas sus
referencias. Adems de cambiar la sintaxis de las definiciones de las funciones, claro.

Declaracin de la plantilla ArbolABB:

Se trata de una simple generalizacin de la clase del punto anterior:

t empl at e<cl ass DATO>
cl ass ABB {
pr i vat e:
/ / / / Cl ase l ocal de Li st a par a Nodo de Ar bol Bi nar i o:
t empl at e<cl ass DATON>
cl ass Nodo {
publ i c:
/ / Const r uct or :
Nodo( const DATON dat , Nodo<DATON> *i zq=NULL, Nodo<DATON> *der =NULL) :
dat o( dat ) , i zqui er do( i zq) , der echo( der ) {}
/ / Mi embr os:
DATON dat o;
Nodo<DATON> *i zqui er do;
Nodo<DATON> *der echo;
};

/ / Punt er os de l a l i st a, par a cabeza y nodo act ual :
74
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Nodo<DATO> *r a z;
Nodo<DATO> *act ual ;
i nt cont ador ;
i nt al t ur a;

publ i c:
/ / Const r uct or y dest r uct or bsi cos:
ABB( ) : r a z( NULL) , act ual ( NULL) {}
~ABB( ) { Podar ( r a z) ; }
/ / I nser t ar en r bol or denado:
voi d I nser t ar ( const DATO dat ) ;
/ / Bor r ar un el ement o del r bol :
voi d Bor r ar ( const DATO dat ) ;
/ / Funci n de bsqueda:
bool Buscar ( const DATO dat ) ;
/ / Compr obar si el r bol est vac o:
bool Vaci o( Nodo<DATO> *r ) { r et ur n r ==NULL; }
/ / Compr obar si es un nodo hoj a:
bool EsHoj a( Nodo<DATO> *r ) { r et ur n ! r - >der echo && ! r - >i zqui er do; }
/ / Cont ar nmer o de nodos:
const i nt Numer oNodos( ) ;
const i nt Al t ur aAr bol ( ) ;
/ / Cal cul ar al t ur a de un dat o:
i nt Al t ur a( const DATO dat ) ;
/ / Devol ver r ef er enci a al dat o del nodo act ual :
DATO &Val or Act ual ( ) { r et ur n act ual - >dat o; }
/ / Mover se al nodo r a z:
voi d Rai z( ) { act ual = r a z; }
/ / Apl i car una f unci n a cada el ement o del r bol :
voi d I nOr den( voi d ( *f unc) ( DATO&) , Nodo<DATO> *nodo=NULL, bool r =t r ue) ;
voi d Pr eOr den( voi d ( *f unc) ( DATO&) , Nodo<DATO> *nodo=NULL, bool r =t r ue) ;
voi d Post Or den( voi d ( *f unc) ( DATO&) , Nodo<DATO> *nodo=NULL, bool r =t r ue) ;
pr i vat e:
/ / Funci ones auxi l i ar es
voi d Podar ( Nodo<DATO>* &) ;
voi d auxCont ador ( Nodo<DATO>*) ;
voi d auxAl t ur a( Nodo<DATO>*, i nt ) ;
};


Definicin de las funciones miembro:

Las definiciones de las funciones miembro de la clase no difieren en nada de las que creamos en el
ejemplo anterior. Tan solo se han sustituido los tipos del dato por el tipo de dato de la plantilla.




75
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
rboles AVL:

rboles Equilibrados:

Ya vimos que el comportamiento de los ABB no es siempre tan bueno como nos gustara. Pues bien,
para minimizar el problema de los ABB desequilibrados, sea cual sea el grado de desequilibrio que
tengan, se puede recurrir a algoritmos de equilibrado de rboles globales. En cuanto a estos algoritmos,
existen varios, por ejemplo, crear una lista mediante la lectura en inorden del rbol, y volver a
reconstruirlo equilibrado. Conociendo el nmero de elementos no es demasiado complicado.

El problema de estos algoritmos es que requieren explorar y reconstruir todo el rbol cada vez que se
inserta o se elimina un elemento, de modo que lo que ganamos al acortar las bsquedas, teniendo que
hacer menos comparaciones, lo perdemos equilibrando el rbol.
Para resolver este inconveniente podemos recurrir a los rboles AVL.

Definicin:

Un rbol AVL (llamado as por las iniciales de sus inventores: Adelson-Velskii y Landis) es un rbol
binario de bsqueda en el que para cada nodo, las alturas de sus subrboles izquierdo y derecho no
difieren en ms de 1.

No se trata de rboles perfectamente equilibrados, pero s son lo suficientemente equilibrados como para
que su comportamiento sea lo bastante bueno como para usarlos donde los ABB no garantizan tiempos
de bsqueda ptimos.

El algoritmo para mantener un rbol AVL equilibrado se basa en reequilibrados locales, de modo que no
es necesario explorar todo el rbol despus de cada insercin o borrado.

Operaciones en AVL:

Los AVL son tambin ABB, de modo que mantienen todas las operaciones que poseen stos. Las nuevas
operaciones son las de equilibrar el rbol, pero eso se hace como parte de las operaciones de insertado y
borrado.

Factor de Equilibrio:

Cada nodo, adems de la informacin que se pretende almacenar, debe tener los dos punteros a los
rboles derecho e izquierdo, igual que los ABB, y adems un miembro nuevo: el factor de equilibrio.
76
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
El factor de equilibrio es la diferencia entre las alturas del rbol derecho y el izquierdo:
FE =altura subrbol derecho - altura subrbol izquierdo;
Por definicin, para un rbol AVL, este valor debe ser -1, 0 1.

Los reequilibrados se realizan mediante rotaciones, en el siguiente punto veremos cada caso, ahora
vamos a ver las cuatro posibles rotaciones que podemos aplicar.

Rotacin simple a la derecha (SD):

Esta rotacin se usar cuando el subrbol izquierdo de un nodo sea 2 unidades ms alto que el derecho,
es decir, cuando su FE sea de -2. Y adems, la raz del subrbol izquierdo tenga una FE de -1, es decir,
que est cargado a la izquierda.

Procederemos del siguiente modo:

Llamaremos P al nodo que muestra el desequilibrio, el que tiene una FE de -2. Y llamaremos Q al nodo
raz del subrbol izquierdo de P. Adems, llamaremos A al subrbol izquierdo de Q, B al subrbol
derecho de Q y C al subrbol derecho de P.
En el grfico que puede observar que tanto B como C tienen la misma altura (n), y A es una unidad
mayor (n+1). Esto hace que el FE de Q sea -1, la altura del subrbol que tiene Q como raz es (n+2) y
por lo tanto el FE de P es -2.
Pasamos el subrbol derecho del nodo Q como subrbol izquierdo de P. Esto mantiene el rbol como
ABB, ya que todos los valores a la derecha de Q siguen estando a la izquierda de P.
El rbol P pasa a ser el subrbol derecho del nodo Q.
Ahora, el nodo Q pasa a tomar la posicin del nodo P, es decir, hacemos que la entrada al rbol sea el
nodo Q, en lugar del nodo P. Previamente, P puede que fuese un rbol completo o un subrbol de otro
nodo de menor altura.

En el rbol resultante se puede ver que tanto P como Q quedan equilibrados en cuanto altura. En el caso
de P porque sus dos subrboles tienen la misma altura (n), en el caso de Q, porque su subrbol izquierdo
A tiene una altura (n+1) y su subrbol derecho tambin, ya que a P se aade la altura de cualquiera de
sus subrboles.

Rotacin simple a la izquierda (SI):

Se trata del caso simtrico del anterior. Esta rotacin se usar cuando el subrbol derecho de un nodo sea
2 unidades ms alto que el izquierdo, es decir, cuando su FE sea de 2. Y adems, la raz del subrbol
derecho tenga una FE de 1, es decir, que est cargado a la derecha.


77
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Procederemos del siguiente modo:
Llamaremos P al nodo que muestra el desequilibrio, el que tiene una FE de 2. Y llamaremos Q al nodo
raz del subrbol derecho de P. Adems, llamaremos A al subrbol izquierdo de P, B al subrbol
izquierdo de Q y C al subrbol derecho de Q.
En el grfico que puede observar que tanto A como B tienen la misma altura (n), y C es una unidad
mayor (n+1). Esto hace que el FE de Q sea 1, la altura del subrbol que tiene Q como raz es (n+2) y por
lo tanto el FE de P es 2.
Pasamos el subrbol izquierdo del nodo Q como subrbol derecho de P. Esto mantiene el rbol como
ABB, ya que todos los valores a la izquierda de Q siguen estando a la derecha de P.
El rbol P pasa a ser el subrbol izquierdo del nodo Q.
Ahora, el nodo Q pasa a tomar la posicin del nodo P, es decir, hacemos que la entrada al rbol sea el
nodo Q, en lugar del nodo P. Previamente, P puede que fuese un rbol completo o un subrbol de otro
nodo de menor altura.

En el rbol resultante se puede ver que tanto P como Q quedan equilibrados en cuanto altura. En el caso
de P porque sus dos subrboles tienen la misma altura (n), en el caso de Q, porque su subrbol izquierdo
A tiene una altura (n+1) y su subrbol derecho tambin, ya que a P se aade la altura de cualquiera de
sus subrboles.


Rotaciones dobles de nodos:

Rotacin doble a la derecha (DD):

Esta rotacin se usar cuando el subrbol izquierdo de un nodo sea 2 unidades ms alto que el derecho,
es decir, cuando su FE sea de -2. Y adems, la raz del subrbol izquierdo tenga una FE de 1, es decir,
que est cargado a la derecha.

Este es uno de los posibles rboles que pueden presentar esta estructura, pero hay otras dos
posibilidades. El nodo R puede tener una FE de -1, 0 1. En cada uno de esos casos los rboles
izquierdo y derecho de R (B y C) pueden tener alturas de n y n-1, n y n, o n-1 y n, respectivamente.
El modo de realizar la rotacin es independiente de la estructura del rbol R, cualquiera de las tres
produce resultados equivalentes. Haremos el anlisis para el caso en que FE sea -1.
En este caso tendremos que realizar dos rotaciones.
Llamaremos P al nodo que muestra el desequilibrio, el que tiene una FE de -2. Llamaremos Q al nodo
raz del subrbol izquierdo de P, y R al nodo raz del subrbol derecho de Q.
Haremos una rotacin simple de Q a la izquierda.
Despus, haremos una rotacin simple de P a la derecha.
Con ms detalle, procederemos del siguiente modo:
Pasamos el subrbol izquierdo del nodo R como subrbol derecho de Q. Esto mantiene el rbol como
ABB, ya que todos los valores a la izquierda de R siguen estando a la derecha de Q.
78
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Ahora, el nodo R pasa a tomar la posicin del nodo Q, es decir, hacemos que la raz del subrbol
izquierdo de P sea el nodo R en lugar de Q.
El rbol Q pasa a ser el subrbol izquierdo del nodo R.

Pasamos el subrbol derecho del nodo R como subrbol izquierdo de P. Esto mantiene el rbol como
ABB, ya que todos los valores a la derecha de R siguen estando a la izquierda de P.
Ahora, el nodo R pasa a tomar la posicin del nodo P, es decir, hacemos que la entrada al rbol sea el
nodo R, en lugar del nodo P. Como en los casos anteriores, previamente, P puede que fuese un rbol
completo o un subrbol de otro nodo de menor altura.
El rbol P pasa a ser el subrbol derecho del nodo R


Rotacin doble a la izquierda (DI):

Esta rotacin se usar cuando el subrbol derecho de un nodo sea 2 unidades ms alto que el izquierdo,
es decir, cuando su FE sea de 2. Y adems, la raz del subrbol derecho tenga una FE de -1, es decir, que
est cargado a la izquierda. Se trata del caso simtrico del anterior.

En este caso tambin tendremos que realizar dos rotaciones.
Llamaremos P al nodo que muestra el desequilibrio, el que tiene una FE de 2. Llamaremos Q al nodo
raz del subrbol derecho de P, y R al nodo raz del subrbol izquierdo de Q.
Haremos una rotacin simple de Q a la derecha.
Despus, haremos una rotacin simple de P a la izquierda.
Con ms detalle, procederemos del siguiente modo:
Pasamos el subrbol derecho del nodo R como subrbol izquierdo de Q. Esto mantiene el rbol como
ABB, ya que todos los valores a la derecha de R siguen estando a la izquierda de Q.
Ahora, el nodo R pasa a tomar la posicin del nodo Q, es decir, hacemos que la raz del subrbol derecho
de P sea el nodo R en lugar de Q.
El rbol Q pasa a ser el subrbol derecho del nodo R.

Pasamos el subrbol izquierdo del nodo R como subrbol derecho de P. Esto mantiene el rbol como
ABB, ya que todos los valores a la izquierda de R siguen estando a la derecha de P.
Ahora, el nodo R pasa a tomar la posicin del nodo P, es decir, hacemos que la entrada al rbol sea el
nodo R, en lugar del nodo P. Como en los casos anteriores, previamente, P puede que fuese un rbol
completo o un subrbol de otro nodo de menor altura.
El rbol P pasa a ser el subrbol izquierdo del nodo R.


Reequilibrados en rboles en rboles AVL:

Cada vez que insertemos o eliminemos un nodo en un rbol AVL pueden suceder dos cosas: que el rbol
se mantenga como AVL o que pierda esta propiedad. En el segundo caso siempre estaremos en uno de
los explicados anteriormente, y recuperaremos el estado AVL aplicando la rotacin adecuada.
79
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Ya comentamos que necesitamos aadir un nuevo miembro a cada nodo del rbol para averiguar si el
rbol sigue siendo AVL, el Factor de Equilibrio. Cada vez que insertemos o eliminemos un nodo
deberemos recorrer el camino desde ese nodo hacia el nodo raz actualizando los valores de FE de cada
nodo. Cuando uno de esos valores sea 2 -2 aplicaremos la rotacin correspondiente.
Debido a que debemos ser capaces de recorrer el rbol en direccin a la raz, aadiremos un nuevo
puntero a cada nodo que apunte al nodo padre. Esto complicar algo las operaciones de insercin,
borrado y rotacin, pero facilita y agiliza mucho el clculo del FE, y veremos que las complicaciones se
compensan en gran parte por las facilidades obtenidas al disponer de este puntero.

Nota: En rigor, no es necesario ese puntero, podemos almacenar el camino que recorremos para
localizar un nodo concreto usando una pila, y despus podemos usar la pila para recuperar el camino en
orden inverso. Pero esto nos obliga a introducir otra estructura dinmica, y segn mi opinin, complica
en exceso el algoritmo.
Cuando estemos actualizando los valores de FE no necesitamos calcular las alturas de las dos ramas de
cada nodo, sabiendo en valor anterior de FE, y sabiendo en qu rama hemos aadido o eliminado el
nodo, es fcil calcular el nuevo valor de FE. Si el nodo ha sido aadido en la rama derecha o eliminado
en la izquierda, y ha habido un cambio de altura en la rama, se incrementa el valor de FE; si el nodo ha
sido aadido en la rama izquierda o eliminado en la derecha, y ha habido un cambio de altura en la rama,
se decrementa el valor de FE.
Los cambios de altura en una rama se producen slo cuando el FE del nodo raz de esa rama ha
cambiado de 0 a 1 de 0 a -1. En caso contrario, cuando el FE cambia de 1 a 0 de -1 a 0, no se produce
cambio de altura.
Si no hay cambio de altura, los valores de FE del resto de los nodos hasta el raz no pueden cambiar,
recordemos que el factor de equilibrio se define como la diferencia de altura entre las ramas derecha e
izquierda de un nodo, la altura de la rama que no pertenece al camino no puede cambiar, puesto que
sigue teniendo los mismos nodos que antes, de modo que si la altura de la rama que pertenece al camino
no cambia, tampoco puede cambiar el valor de FE.
Por ejemplo, supongamos que en siguiente rbol AVL insertamos el nodo de valor 8:

Para empezar, cualquier nodo nuevo ser un nodo hoja, de modo que su FE ser siempre 0.
Ahora actualizamos el valor de FE del nodo padre del que acabamos de insertar (P). El valor previo es 0,
y hemos aadido un nodo en su rama izquierda, por lo tanto, el nuevo valor es -1. Esto implica un
cambio de altura, por lo tanto, continuamos camino hacia la raz.
A continuacin tomamos el nodo padre de P (Q), cuyo valor previo de FE era 1, y al que tambin hemos
aadido un nodo en su rama izquierda, por lo tanto decrementamos ese valor, y el nuevo ser 0. En este
caso no ha incremento de altura, la altura del rbol cuya raz es Q sigue siendo la misma, por lo tanto,
ninguno de los valores de FE de los nodos hasta el raz puede haber cambiado. Es decir, no necesitamos
seguir recorriendo el camino.
Si verificamos el valor de FE del nodo R vemos que efectivamente se mantiene, puesto que tanto la
altura del subrbol derecho como del izquierdo, siguen siendo las mismas.

Pero algunas veces, el valor de FE del nodo es -2 2, son los casos en los que perdemos la propiedad
AVL del rbol, y por lo tanto tendremos que recuperarla.
80
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Reequilibrados en rboles AVL por insercin de un nodo

En ese caso, cuando el valor de FE de un nodo tome el valor -2 2, no seguiremos el camino, sino que,
con el valor de FE de el nodo actual y el del nodo derecho si FE es 2 o el del nodo izquierdo si es -2,
determinaremos qu tipo de rotacin debemos hacer.
FE nodo actual FE del nodo derecho FE del nodo izquierdo Rotacin
-2 No importa -1 RSD
-2 No importa 1 RDD
2 -1 No importa RDI
2 1 No importa RSI

El resto de los casos no nos interesan. Esto es porque en nodos desequilibrados hacia la derecha, con
valores de FE positivos, siempre buscaremos el equilibrio mediante rotaciones a la izquierda, y
viceversa, con nodos desequilibrados hacia la izquierda, con valores de FE negativos, buscaremos el
equilibrio mediante rotaciones a la derecha.
Supongamos que el valor de FE del nodo ha pasado de -1 a -2, debido a que se ha aadido un nodo. Esto
implica que el nodo aadido lo ha sido en la rama izquierda, si lo hubiramos aadido en la derecha el
valor de FE nunca podra decrecer.

Reequilibrados en rboles AVL por borrado de un nodo:

Cuando el desequilibrio se debe a la eliminacin de un nodo la cosa puede ser algo diferente, pero
veremos que siempre se puede llegar a uno de los casos anteriores.
Supongamos el siguiente ejemplo, en el rbol AVL eliminaremos el nodo de valor 3:

El valor de FE del nodo P pasa de 1 a 2, sabemos que cuando el valor de FE de un nodo es 2 siempre
tenemos que aplicar una rotacin a izquierdas. Para saber cual de las dos rotaciones debemos aplicar
miramos el valor de FE del nodo derecho. Pero en este caso, el valor de FE de ese nodo es 0. Esto no
quiere decir que no podamos aplicar ninguna de las rotaciones, por el contrario, podremos aplicar
cualquiera de ellas. Aunque por economa, lo razonable es aplicar la rotacin simple.

Del mismo modo, el valor de FE del nodo derecho podra haber sido 1 -1, en ese caso s est
determinado el tipo de rotacin a realizar.
El razonamiento es similar cuando se eliminan nodos y el resultado es que se obtiene un nodo con FE de
-2, en este caso se realizar una rotacin a derechas, y la rotacin depender del valor de FE del nodo
izquierdo al que muestra el desequilibrio. Si es 0 -1 haremos una rotacin simple, si es 1, haremos una
rotacin doble.
Tendremos entonces una tabla ms general para decidir la rotacin a aplicar:
FE nodo actual FE del nodo derecho FE del nodo izquierdo Rotacin
-2 No importa -1 RSD
81
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
-2 No importa 0 RSD
-2 No importa 1 RDD
2 -1 No importa RDI
2 0 No importa RSI
2 1 No importa RSI


Los rboles AVL siempre quedan equilibrados despus de una rotacin:

Esto puede comprobarse analizando los mtodos de rotacin que hemos estudiado, despus de efectuada
la rotacin, la altura del rbol cuya raz es el nodo rotado se mantiene, por lo tanto, no necesitamos
continuar el camino hacia la raz: sabemos que el rbol es AVL.


Algoritmos:

De insercin de nodo:

En general, la insercin de nodos en un rbol AVL es igual que en un rbol ABB, la diferencia es que en
un rbol AVL, despus de insertar el nodo debemos recorrer el rbol en sentido hacia la raz,
recalculando los valores de FE, hasta que se cumpla una de estas condiciones: que lleguemos a la raz,
que se encuentre un nodo con valor de FE de 2, -2, o que se llegue a un nodo cuyo FE no cambie o
decrezca en valor absoluto, es decir, que cambie de 1 a 0 de -1 a 0.
Podemos considerar que el algoritmo de insercin de nodos en rboles AVL es una ampliacin del que
vimos para rboles ABB.

De borrado de nodo:

Lo mismo pasa cuando se eliminan nodos, el algoritmo es el mismo que en rboles ABB, pero despus
de eliminar el nodo debemos recorrer el camino hacia la raz recalculando los valores de FE, y
equilibrando el rbol si es necesario.

De recalcular FE:

Ya comentamos ms atrs que para seguir el camino desde el nodo insertado o borrado hasta el nodo raz
tenemos dos alternativas:
Guardar en una pila los punteros a los nodos por los que hemos pasado para llegar al nodo insertado o
borrado, es decir, almacenar el camino.
82
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
Aadir un nuevo puntero a cada nodo que apunte al padre del nodo actual. Esto nos permite recorrer el
rbol en el sentido contrario al normal, es decir, en direccin a la raz.
Para calcular los nuevos valores de FE de los nodos del camino hay que tener en cuenta los siguientes
hechos:
El valor de FE de un nodo insertado es cero, ya que siempre insertaremos nodos hoja.
Si el nuevo valor de FE para cualquiera de los siguientes nodos del camino es cero, habremos terminado
de actualizar los valores de FE, ya que la rama mantiene su altura, la insercin o borrado del nodo no
puede influir en los valores de FE de los siguientes nodos del camino.
Cuando se elimine un nodo pueden pasar dos cosas. Siempre eliminamos un nodo hoja, ya que cuando
no lo es, lo intercambiamos con un nodo hoja antes de eliminarlo. Pero algunas veces, el nodo padre del
nodo eliminado se convertir a su vez en nodo hoja, y en ese caso no siempre hay que dar por terminada
la actualizacin del FE del camino. Por lo tanto, cuando eliminemos un nodo, actualizaremos el valor de
FE del nodo padre y continuaremos el camino, independientemente del valor de FE calculado.
A la hora de actualizar el valor de FE de un nodo, tenemos que distinguir cuando el equilibrado sea
consecuencia de una insercin o lo sea de una eliminacin. Incrementaremos el valor de FE del nodo si
la insercin fue en la rama derecha o si la eliminacin fue en la rama izquierda, decrementaremos si la
insercin fue en la izquierda o la eliminacin en la derecha.
Si en valor de FE es -2, haremos una rotacin doble a la derecha su el valor de FE del nodo izquierdo es
1, y simple si es 1 0.
Si en valor de FE es 2, haremos una rotacin doble a la izquierda su el valor de FE del nodo izquierdo es
-1, y simple si es -1 0.
En cualquiera de los dos casos, podremos dar por terminado el recorrido del camino, ya que la altura del
rbol cuya raz es un nodo rotado no cambia.
En cualquier otro caso, seguiremos actualizando hasta llegar al nodo raz.

De rotacin simple:

A la hora de implementar los algoritmos que hemos visto para rotaciones simples tenemos dos opciones:
seguir literalmente los pasos de los grficos, o tomar un atajo, y hacerlo mediante asignaciones. Nosotros
lo haremos del segundo modo, ya que resulta mucho ms rpido y sencillo.
Primero haremos las reasignaciones de punteros, de modo que el rbol resultante responda a la estructura
despus de la rotacin. Despus actualizaremos los punteros al nodo padre para los nodos que han
cambiado de posicin. Por ltimo actualizaremos los valores de FE de esos mismos nodos.
Para la primera fase usaremos punteros auxiliares a nodo, que en el caso de rotacin a la derecha
necesitamos un puntero P al nodo con FE igual a -2. Ese ser el parmetro de entrada, otro puntero al
nodo izquierdo de P: Q. Y tres punteros ms a los rboles A, B y C.

En realidad, si nos fijamos en los grficos, los punteros a A y C no son necesarios, ya que ambos
conservan sus posiciones, A sigue siendo el subrbol izquierdo de Q y C el subrbol derecho de P.
Usaremos otro puntero ms: Padre, que apunte al padre de P. Disponiendo de los punteros Padre, P, Q y
B, realizar la rotacin es muy sencillo:

i f ( Padr e)
i f ( Padr e- >der echo == P) Padr e- >der echo = Q;
83
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
el se Padr e- >i zqui er do = Q;
el se r a z = Q;

/ / Reconst r ui r r bol :
P- >i zqui er do = B;
Q- >der echo = P;

Hay que tener en cuenta que P puede ser la raz de un subrbol derecho o izquierdo de otro nodo, o
incluso la raz del rbol completo. Por eso comprobamos si P tiene padre, y si lo tiene, cual de sus ramas
apunta a P, cuando lo sabemos, hacemos que esa rama apunte a Q. Si Padre es NULL, entonces P era la
raz del rbol, as que hacemos que la nueva raz sea Q.
Slo nos queda trasladar el subrbol B a la rama izquierda de P, y Q a la rama derecha de P.
La segunda fase consiste en actualizar los punteros padre de los nodos que hemos cambiado de posicin:
P, B y Q.

P- >padr e = Q;
i f ( B) B- >padr e = P;
Q- >padr e = Padr e;

El padre de P es ahora Q, el de Q es Padre, y el de B, si existe es P.
La tercera fase consiste en ajustar los valores de FE de los nodos para los que puede haber cambiado.
Esto es muy sencillo, despus de una rotacin simple, los nicos valores de FE que cambian son los de P
y Q, y ambos valen 0.

/ / Rot aci n si mpl e a der echas
voi d RSD( Nodo* nodo)
{
Nodo *Padr e = nodo- >padr e;
Nodo *P = nodo;
Nodo *Q = P- >i zqui er do;
Nodo *B = Q- >der echo;

i f ( Padr e)
i f ( Padr e- >der echo == P) Padr e- >der echo = Q;
el se Padr e- >i zqui er do = Q;
el se r a z = Q;

/ / Reconst r ui r r bol :
P- >i zqui er do = B;
Q- >der echo = P;

/ / Reasi gnar padr es:
P- >padr e = Q;
i f ( B) B- >padr e = P;
Q- >padr e = Padr e;

/ / Aj ust ar val or es de FE:
P- >FE = 0;
Q- >FE = 0;
}
84
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
La rotacin a izquierdas es simtrica.

De rotacin doble:

Para implementar las rotaciones dobles trabajaremos de forma anloga.
Primero haremos las reasignaciones de punteros, de modo que el rbol resultante responda a la estructura
despus de la rotacin. Despus actualizaremos los punteros al nodo padre para los nodos que han
cambiado de posicin. Por ltimo actualizaremos los valores de FE de esos mismos nodos.
Para la primera fase usaremos punteros auxiliares a nodo, que en el caso de rotacin a la derecha
necesitamos un puntero P al nodo con FE igual a -2. Ese ser el parmetro de entrada, otro puntero al
nodo izquierdo de P: Q. Un tercero al nodo derecho de Q: R. Y cuatro punteros ms a los rboles A, B,
C y D.

En realidad, si nos fijamos en los grficos, los punteros a A y D no son necesarios, ya que ambos
conservan sus posiciones, A sigue siendo el subrbol izquierdo de Q y D el subrbol derecho de P.
Tambin en este caso usaremos otro puntero ms: Padre, que apunte al padre de P. Disponiendo de los
punteros Padre, P, Q, R, B y C, realizar la rotacin es muy sencillo:

i f ( Padr e)
i f ( Padr e- >der echo == nodo) Padr e- >der echo = R;
el se Padr e- >i zqui er do = R;
el se r a z = R;

/ / Reconst r ui r r bol :
Q- >der echo = B;
P- >i zqui er do = C;
R- >i zqui er do = Q;
R- >der echo = P;

Ahora tambin hay que tener en cuenta que P puede ser la raz de un subrbol derecho o izquierdo de
otro nodo, o incluso la raz del rbol completo. Por eso comprobamos si P tiene padre, y si lo tiene, cual
de sus ramas apunta a P, cuando lo sabemos, hacemos que esa rama apunte a R. Si Padre es NULL,
entonces P era la raz del rbol, as que hacemos que la nueva raz sea R.
Slo nos queda trasladar el subrbol B a la rama derecha de Q, C a la rama izquierda de P, Q a la rama
izquierda de R y P a la rama derecha de R.
La segunda fase consiste en actualizar los punteros padre de los nodos que hemos cambiado de posicin:
P, Q, R, B y C.

R- >>padr e = Padr e;
P- >padr e = Q- >padr e = R;
i f ( B) B- >padr e = Q;
i f ( C) C- >padr e = P;
El padre de R es ahora Padre, el de P y Q es R, y el de B, si existe es Q, y el de C, si existe, es P.
La tercera fase consiste en ajustar los valores de FE de los nodos para los que puede haber cambiado.
85
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
En las rotaciones dobles esto se complica un poco ya que puede suceder que el valor de FE de R antes de
la rotacin sea -1, 0 o 1. En cada caso, los valores de FE de P y Q despus de la rotacin sern
diferentes.

/ / Aj ust ar val or es de FE:
swi t ch( R- >FE) {
case - 1: Q- >FE = 0; P- >FE = 1; br eak;
case 0: Q- >FE = 0; P- >FE = 0; br eak;
case 1: Q- >FE = - 1; P- >FE = 0; br eak;
}
R- >FE = 0;

Si la altura de B es n-1 y la de C es n, el valor de FE de R es 1. Despus de la rotacin, la rama B pasa a
ser el subrbol derecho de Q, por lo tanto, la FE de Q, dado que la altura de su rama izquierda es n, ser
0. La rama C pasa a ser el subrbol izquierdo de P, y dado que la altura de la rama derecha es n, la FE de
P ser -1.
Si la altura de B es n y la de C es n-1, el valor de FE de R es -1. Despus de la rotacin, la rama B pasa a
ser el subrbol derecho de Q, por lo tanto, la FE de Q, dado que la altura de su rama izquierda es n, ser
0. La rama C pasa a ser el subrbol izquierdo de P, y dado que la altura de la rama derecha es n, la FE de
P ser 0.
Por ltimo, si la altura de B y C es n, el valor de FE de R es 0. Despus de la rotacin, la rama B pasa a
ser el subrbol derecho de Q, por lo tanto, la FE de Q, dado que la altura de su rama izquierda es n, ser
0. La rama C pasa a ser el subrbol izquierdo de P, y dado que la altura de la rama derecha es n, la FE de
P ser 0.

/ / Rot aci n dobl e a der echas
voi d RDD( Nodo* nodo)
{
Nodo *Padr e = nodo- >padr e;
Nodo *P = nodo;
Nodo *Q = P- >i zqui er do;
Nodo *R = Q- >der echo;
Nodo *B = R- >i zqui er do;
Nodo *C = R- >der echo;

i f ( Padr e)
i f ( Padr e- >der echo == nodo) Padr e- >der echo = R;
el se Padr e- >i zqui er do = R;
el se r a z = R;

/ / Reconst r ui r r bol :
Q- >der echo = B;
P- >i zqui er do = C;
R- >i zqui er do = Q;
R- >der echo = P;

/ / Reasi gnar padr es:
R- >padr e = Padr e;
P- >padr e = Q- >padr e = R;
i f ( B) B- >padr e = Q;
i f ( C) C- >padr e = P;
86
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
/ / Aj ust ar val or es de FE:
swi t ch( R- >FE) {
case - 1: Q- >FE = 0; P- >FE = 1; br eak;
case 0: Q- >FE = 0; P- >FE = 0; br eak;
case 1: Q- >FE = - 1; P- >FE = 0; br eak;
}
R- >FE = 0;
}

Ejemplo de rbol AVL en C:

No ha demasiado que aadir, construiremos los ejemplos de rboles AVL basndonos en los ejemplos
que hicimos para rboles binarios de bsqueda.
Slo tenemos que aadir cinco nuevas funciones: una para equilibrar el rbol, y cuatro para las cuatro
posibles rotaciones.
Adems, modificaremos ligeramente las funciones de insercin y borrado para que se equilibre el rbol
automticamente despus de cada insercin o borrado.
En la estructura de nodo para rbol AVL aadiremos un puntero al nodo padre y un valor entero para
almacenar el factor de equilibrio.
Cuando se inserten nuevos nodos hay que ajustar los valores de los nuevos miembros de nodo: FE y
padre. Seguidamente, llamaremos a la funcin "Equilibrar", salvo que el nodo insertado sea el raz, ya
que en ese caso no es necesario, evidentemente.
El procedimiento de equilibrar consiste en la implementacin del algoritmo que vimos en el punto
anterior:

/ * Equi l i br ar r bol AVL par t i endo de un nodo*/
voi d Equi l i br ar ( Ar bol *a, pNodo nodo, i nt r ama, i nt nuevo)
{
i nt sal i r = FALSE;

/ * Recor r er cami no i nver so act ual i zando val or es de FE: */
whi l e( nodo && ! sal i r ) {
i f ( nuevo)
i f ( r ama == I ZQUI ERDO) nodo- >FE- - ; / * Depende de si aadi mos . . . */
el se nodo- >FE++;
el se
i f ( r ama == I ZQUI ERDO) nodo- >FE++; / * . . . o bor r amos */
el se nodo- >FE- - ;
i f ( nodo- >FE == 0) sal i r = TRUE; / * La al t ur a de l as r ama que
empi eza en nodo no ha var i ado,
sal i r de Equi l i br ar */
el se i f ( nodo- >FE == - 2) { / * Rot ar a der echas y sal i r : */
i f ( nodo- >i zqui er do- >FE == 1) RDD( a, nodo) ; / * Rot aci n dobl e */
el se RSD( a, nodo) ; / * Rot aci n si mpl e */
sal i r = TRUE;
}
el se i f ( nodo- >FE == 2) { / * Rot ar a i zqui er das y sal i r : */
i f ( nodo- >der echo- >FE == - 1) RDI ( a, nodo) ; / * Rot aci n dobl e */
el se RSI ( a, nodo) ; / * Rot aci n si mpl e */
sal i r = TRUE;
87
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
}
i f ( nodo- >padr e)
i f ( nodo- >padr e- >der echo == nodo) r ama = DERECHO; el se r ama = I ZQUI ERDO;
nodo = nodo- >padr e; / * Cal cul ar FE, si gui ent e nodo del cami no. */
}
}

Las funciones para rotar son tambin sencillas, por ejemplo, la rotacin simple a la derecha:

/ * Rot aci n si mpl e a der echas */
voi d RSD( Ar bol *a, pNodo nodo)
{
pNodo Padr e = nodo- >padr e;
pNodo P = nodo;
pNodo Q = P- >i zqui er do;
pNodo B = Q- >der echo;

i f ( Padr e)
i f ( Padr e- >der echo == P) Padr e- >der echo = Q;
el se Padr e- >i zqui er do = Q;
el se *a = Q;

/ * Reconst r ui r r bol : */
P- >i zqui er do = B;
Q- >der echo = P;

/ * Reasi gnar padr es: */
P- >padr e = Q;
i f ( B) B- >padr e = P;
Q- >padr e = Padr e;

/ * Aj ust ar val or es de FE: */
P- >FE = 0;
Q- >FE = 0;
}

Y la rotacin doble a la derecha:

/ * Rot aci n dobl e a der echas */
voi d RDD( Ar bol *r a z, Ar bol nodo)
{
pNodo Padr e = nodo- >padr e;
pNodo P = nodo;
pNodo Q = P- >i zqui er do;
pNodo R = Q- >der echo;
pNodo B = R- >i zqui er do;
pNodo C = R- >der echo;

i f ( Padr e)
i f ( Padr e- >der echo == nodo) Padr e- >der echo = R;
el se Padr e- >i zqui er do = R;
el se *r a z = R;

/ * Reconst r ui r r bol : */
Q- >der echo = B;
88
ESTRUCTURAS DINMICAS DE DATOS EN LENGUAJ E C
Ismael Camarero S. http://programandoenc.webcindario.com
P- >i zqui er do = C;
R- >i zqui er do = Q;
R- >der echo = P;

/ * Reasi gnar padr es: */
R- >padr e = Padr e;
P- >padr e = Q- >padr e = R;
i f ( B) B- >padr e = Q;
i f ( C) C- >padr e = P;

/ * Aj ust ar val or es de FE: */
swi t ch( R- >FE) {
case - 1: Q- >FE = 0; P- >FE = 1; br eak;
case 0: Q- >FE = 0; P- >FE = 0; br eak;
case 1: Q- >FE = - 1; P- >FE = 0; br eak;
}
R- >FE = 0;
}


Bibliografa:

Es muy aconsejable el uso del texto:

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

Luis J oyanes Aguilar e Ignacio Zahonero Martnez.
Editorial: McGraw-Hill



Tutorial bajado de:

http://programandoenc.webcindario.com
89

Das könnte Ihnen auch gefallen