Professional Documents
Culture Documents
1. INTRODUCCIN.
Hasta ahora las estructuras de datos que hemos estudiado eran de tipo lineal, o
sea,exista una relacin de anterior y siguiente entre los elementos que la
componan(cada elemento tendr uno anterior y otro posterior , salvo los casos de
primero y ltimo).Pues bien, aqu se va a estudiar una estructuracin de los datos
ms compleja: los rboles.
Este tipo de estructura es usual incluso fuera del campo de la informtica.El
lector seguramente conoce casos como los rboles gramaticales para analizar
oraciones,los rboles genealgicos ,representacin de jerarquas,etc...La
estructuracin en rbol de los elementos es fundamental dentro del campo de la
informtica aplicndose en una amplia variedad de problemas como veremos ms
adelante.
En principio podemos considerar la estructura de rbol de manera intuitiva como
una estructura jerrquica.Por tanto,para estructurar un conjunto de elementos
ei en rbol, deberemos escoger uno de ellos e1 al que llamaremos raz del
rbol.Del resto de los elementos se selecciona un subconjunto
e2,...,ek estableciendo una relacin padre-hijo entre la raz y cada uno de dichos
elementos de manera que e1 es llamado el padre de e2,de e3,...ek y cada uno de
ellos es llamado un hijo de e1.Iterativamente podemos realizar la misma
operacin para cada uno de estos elementos asignando a cada uno de ellos un
nmero de 0 o ms hijos hasta que no tengamos ms elementos que insertar.El
nico elemento que no tiene padre es e1,la raz del rbol.Por otro lado hay un
conjunto de elementos que no tienen hijos aunque s padre que son llamados
hojas.Como hemos visto la relacin de paternidad es una relacin uno a muchos.
Para tratar esta estructura cambiaremos la notacin:
Usando esta notacin,un rbol tiene uno y slo un nodo raz y uno o ms nodos
hoja.
Desde un punto de vista formal la estructura de datos rbol es un caso particular
de grafo, ms concretamente,en la teora de grafos se denota de forma similar
comorbol dirigido. A pesar de ello,la definicin formal ms usual de rbol en
ciencias de la computacin es la recursiva:
etc...
RECORRIDOS DE UN RBOL.
11. El listado por niveles es: desde i=0 hasta la altura h del rbol,listar
de izquierda a derecha los elementos de profundidad i.Como
podemos observar,un nodo n1 aparece antes que n2 en el listado por
niveles si la profundidad de n1 es menor que la profundidad
de n2 usando el orden de los nodos definido anteriormente para el
caso en que tengan la misma profundidad.
Como ejemplo de listados veamos el resultado que se obtendra sobre el
rbol A de la figura 3.
Los resultados de los listados de preorden,postorden e inorden son los
siguientes:
Para que un rbol represente una expresin,hay que tener en cuenta que:
/*Por ejemplo*/
Esta representacin usa la propiedad de los rboles de que cada nodo tiene un
nico padre.Con esta representacin el padre de un nodo puede encontrarse en
tiempo constante.Un camino hacia arriba en el rbol puede seguirse atravesando
el rbol en tiempo proporcional al nmero de nodos en el camino.Podemos
soportar tambin el operador ETIQUETA aadiendo otra matriz L ,tal que L[i] es
la etiqueta del nodo i ,o haciendo que los elementos de la matriz A sean registros
consistiendo en un entero(cursor)y una etiqueta.EJEMPLO:Vase el rbol de la
figura 7:
Hay una matriz de celdas de cabecera indexadas por nodos ,que suponemos
numerados 0,1,2,...,n-1. Cada punto de cabecera apunta a una lista enlazada de
elementos que son nodos.Los elementos sobre una lista encabezada
por cabecera[i] son los hijos de i(por ejemplo, 9 y 4 son los hijos de 8).Si
desarrollamos la estructura de datos que necesitamos en trminos de un tipo de
dato abstracto tLista (de nodos) y damos una implementacin particular de
listas,puede verse como las abstracciones encajan.
#include
#define MAXNODOS 100
#define NODO_NULO -1
/*Definidas apropiadamente*/
/*Por ejemplo*/
destruir(T->hizqda);
destruir(T->herdrcha);
free(T);
}
}
void Insertar_hijo_izqda(nodo n,tArbol Ti,tArbol T)
{
Ti->herdrcha=n->hizqda;
Ti->padre=n;
n->hizqda=Ti;
}
void Insertar_hermano_drcha(nodo n,tArbol Td,tArbol T)
{
if(n==raizAr(T)){
error("Memoria Insuficiente.");
}
Td->herdrcha=n->herdrcha;
Td->padre=n->padre;
n->herdrcha=Td;
}
tArbol Podar_hijo_izqda(nodo n,tArbol T)
{
tArbol Taux;
Taux=n->hizqda;
if(Taux!=ARBOL_VACIO){
n->hizqda=Taux->herdrcha;
Taux->padre=NODO_NULO;
Taux->herdrcha=NODO_NULO;
}
return Taux;
}
tArbol Podar_hermano_drcha(nodo n,tArbol T)
{
tArbol Taux;
Taux=n->herdrcha;
if(Taux!=ARBOL_VACIO){
n->herdrcha=Taux->herdrcha;
Taux->padre=NODO_NULO;
Taux->herdrcha=NODO_NULO;
}
return Taux;
}
La llamada a la funcin tendra como parmetros una etiqueta para el nodo raz
del rbol resultante y una lista de nodos que podra ser vaca en cuyo caso el
rbol que resulta tiene un nico nodo:su raz con etiqueta et. Por ltimo,despus
de dicha lista,es necesario un parmetro adicional(NULL) que indica el final de
la lista tras cuya lectura el procedimiento dejara de aadir ms hijos al nodo raz
que se est construyendo.
IMPLEMENTACIN DE LOS RECORRIDOS DE UN RBOL
Recordemos que los recorridos de un rbol pueden ser de una forma directa en
Preorden, Inorden y Postorden.A continuacin veremos la implementacin de
estos tres recorridos. As mismo,veremos un procedimiento de lectura de un rbol
en preorden.
PREORDEN
1. Visitar la raz.
En esta funcin hemos supuesto que existe una rutina Escribir que tiene como
parmetro de entrada un valor de tipo tEtiqueta que se encarga de imprimir en la
salida estndar.Por ejemplo,si hemos realizado typedef int tEtiqueta la funcin
podra ser la siguiente:
void Escribir(tEtiqueta et)
{
fprintf(stdout,"%d",(int)et);
}
Por otro lado,en los programas C hemos usado el operador de desigualdad entre
un dato de tipo nodo y la constante ARBOL_VACIO.Para hacerlo ms
independiente de la impementacin sera conveniente programar una funcin que
podramos llamar Arbol_Vacio que se aadira como una nueva primitiva que nos
devuelve si el subrbol que cuelga del nodo es un rbol vaco.
Para el procedimiento no recursivo,usaremos una pila para encontrar el camino
alrededor del rbol.El tipo PILA es realmente pila de nodos,es decir,pila de
posiciones de nodos. La idea bsica subyacente al algoritmo es que cuando
estamos en la posicin p,la pila alojar el camino desde la raz a p,con la raz en
el fondo de la pila y el nodo p a la cabeza.El programa tiene dos modos de
operar.En el primer modo desciende por el camino ms a la izquierda en el
rbol,escribiendo y apilando los nodos a lo largo del camino,hasta que encuentra
una hoja.A continuacin el programa entra en el segundo modo de operacin en
el cual vuelve hacia atrs por el camino apilado en la pila,extrayendo los nodos
de la pila hasta que se encuentra un nodo en el camino con un hermano a la
derecha.Entonces el programa vuelve al primer modo de operacin,comenzando
el descenso desde el inexplorado hermano de la derecha.El programa comienza
en modo uno en la raz y termina cuando la pila est vaca.
void PreordenArbol(tArbol T)
{
pila P;
/*Pila de posiciones:tElemento de la pila es el tipo nodo*/
nodo m;
P=CREAR(); /*Funcion de creacion del TDA PILA*/
m=raizAr(T);
do{
if(m!=NODO_NULO){
Escribir(etiquetaAr(n,T));
PUSH(m,P);
m=hizqdaAr(m,T);
}
else if(!VACIA(P)){
m=herdrchaAr(TOPE(P),T);
POP(P);
}
}while(!VACIA(P));
DESTRUIR(P);
INORDEN
1. Recorrer el subrbol ms a la izquierda en inorden.
2. Visitar la raz.
POSTORDEN
1. Recorrer el subrbol ms a la izquierda en postorden.
3. Visitar la raz.
LECTURA
A continuacin veremos un procedimiento que nos realizar la lectura de los
nodos de un rbol introducindolos en preorden.La funcin implementada se
llamaLectura aunque se listan dos funciones(la rutina Lectura2 es una funcin
auxiliar que es usada por la primera).
void Lectura2(nodo n,tArbol T)
{
tEtiqueta etHijo,etHermano;
tArbol Hijo,Hermano;
fprintf(stdout,"Introduce hijo_izqda de:
Escribir(etiquetaAr(n,T));
Leer(&etHijo);
if(comparar(etHijo,FINAL)){
Hijo=creaRaiz(etHijo);
insertar_hijo_izqda(n,Hijo,T);
Lectura2(hizqdaAr(n,T),T);
}
");
");
if(comparar(etHermano,FINAL)){
Hermano=creaRaiz(etHermano);
insertar_hermano_drcha(n,Hermano,T);
Lectura2(herdrchaAr(n,T),T);
}
}
tArbol Lectura()
{
tArbol T;
tEtiqueta et;
fprintf(stdout,"En caso de que no exista el hijo_izqdo o el"
"hermano_derecho introducir el valor: ");
Escribir(FINAL);
/*FINAL actua de centinela*/
fprintf(stdout,"\nIntroduce la raiz del arbol: ");
Leer(&et);
T=creaRaiz(et);
Lectura2(raizAr(T),T);
}
Hemos supuesto que existe una funcin Leer que tiene como parmetro de
entrada un puntero a una zona de memoria que almacena un valor de
tipo tEtiqueta,y que sirve para leer de la entrada estndar un dato de ese
tipo y almacenarlo en dicha zona de memoria.
Existe una variable FINAL que contiene un valor para la etiqueta que "no
es legal" para indicar la inexistencia de un hijo a la izquierda y/o de un
hermano a la derecha.
Suponemos que existe una funcin comparar que tiene como parmetros
de entrada dos variables de tipo tEtiqueta y que devuelve un valor entero
distinto de 0 en caso de que las variables sean distintas segn el criterio
implementado en la funcin.