You are on page 1of 33

Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I

14. RBOLES

14.1 FUNDAMENTOS Y TERMINOLOGA BSICA............................................................................................................. 83


14.2. RBOLES BINARIOS .............................................................................................................................................. 85
14.3. FUNDAMENTOS .................................................................................................................................................... 86
14.4. OPERACIONES CON RBOLES BINARIOS: RECORRIDO DE RBOLES BINARIOS ....................................................... 87
14.5. OTRAS OPERACIONES CON RBOLES BINARIOS..................................................................................................... 89
14.6. REPRESENTACIN DE LOS RBOLES BINARIOS ..................................................................................................... 92
Representacin mediante arrays ............................................................................................................................. 92
Representacin mediante punteros.......................................................................................................................... 93
Constructores de la clase ................................................................................................................................ 94
Sobrecarga del operador = ............................................................................................................................. 95
Mtodo HacerArbol................................................................................................................................. 96
Mtodos de consulta ...................................................................................................................................... 96
Destructor de la clase ..................................................................................................................................... 97
Eliminacin de nodos en un rbol binario...................................................................................................... 97
14.7. REPRESENTACIN DE RBOLES GENERALES COMO RBOLES BINARIOS ............................................................. 101
14.8. RBOLES BINARIOS DE BSQUEDA ..................................................................................................................... 102
Operaciones en rboles binarios de bsqueda...................................................................................................... 103
Bsqueda ............................................................................................................................................................................. 103
Insercin .............................................................................................................................................................................. 105
Borrado ................................................................................................................................................................................ 106
14.9. HEAPS (MONTCULOS) ........................................................................................................................................ 108
Representacin de montculos ............................................................................................................................... 109
Operaciones bsicas sobre montculos ................................................................................................................. 109
Insercin .............................................................................................................................................................................. 110
Borrar (una posicin pos cualquiera) ................................................................................................................................ 111
Aplicacin de los montculos ................................................................................................................................. 111
14.11. ORDENACIN CON RBOLES ............................................................................................................................ 114

14.1 Fundamentos y terminologa bsica


Hasta ahora hemos visto estructuras de datos lineales, es decir, los datos estaban estructurados en
forma de secuencia. Sin embargo, las relaciones entre los objetos no siempre son tan simples como
para ser representadas mediante secuencias (incluso, en ocasiones, es conveniente que no sea as),
sino que la complejidad de las relaciones entre los elementos puede requerir otro tipo de estructura.
En esas situaciones se pasara a tener estructuras de datos no lineales. ste es el caso de la
estructura de datos conocida como rbol.
Los rboles establecen una estructura jerrquica entre los objetos. Los rboles genealgicos y los
organigramas son ejemplos comunes de rboles.
Un rbol es una coleccin de elementos llamados nodos, uno de los cuales se distingue como raz,
junto con una relacin que impone una estructura jerrquica entre los nodos. Formalmente, un rbol
se puede definir de manera recursiva como sigue:
Definicin: Una estructura de rbol con tipo base Valor es:
(i) Bien la estructura vaca.
(ii) Un conjunto finito de uno o ms nodos, tal que existe un nodo especial, llamado
nodo raz, y donde los restantes nodos estn separados en n0 conjuntos disjuntos,
cada uno de los cuales es a su vez un rbol (llamados subrboles del nodo raz).
La definicin implica que cada nodo del rbol es raz de algn subrbol contenido en el rbol
principal.
Tema 14. rboles 83
Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
Ejemplos de estructuras arborescentes:

Figura 1: Ejemplo de rbol.


El ndice de un libro tambin se puede representar en forma de rbol, en esta representacin se
reflejan con claridad las relaciones de dependencia entre cada una de las partes del libro:

Figura 2: Estructura arborescente representando la estructura de un libro.


Antes de continuar avanzando en las caractersticas y propiedades de los rboles, veamos algunos
trminos importantes asociados con el concepto de rbol:
- Grado de un nodo: Es el nmero de subrboles que tienen como raz ese nodo (el
nmero de subrboles que "cuelgan" del nodo).
- Nodo terminal: Nodo con grado 0, no tiene subrboles.
- Grado de un rbol: Grado mximo de los nodos de un rbol.
- Hijos de un nodo: Nodos que dependen directamente de ese nodo, es decir, las races
de sus subrboles.
- Padre de un nodo: Antecesor directo de un nodo, nodo del que depende directamente.
- Nodos hermanos: Nodos hijos del mismo nodo padre.
- Camino: Sucesin de nodos del rbol n1, n2, ..., nk, tal que ni es el padre de ni+1.
- Antecesores de un nodo: Todos los nodos en el camino desde la raz del rbol hasta ese
nodo.
- Nivel de un nodo: Longitud del camino desde la raz hasta el nodo. El nodo raz tiene
nivel 1.
- Altura (profundidad) de un rbol: Nivel mximo de un nodo en un rbol.
- Longitud de camino de un rbol: Suma de las longitudes de los caminos a todos sus
componentes.
- Bosque: Conjunto de n>0 rboles disjuntos.
84 Ricardo Ferrs / Jess Albert
Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
La representacin de un rbol general depender de su grado, es decir, del nmero de relaciones
mximo que puede tener un nodo del rbol. Resulta ms simple la representacin y manipulacin de
una estructura rbol cuando el grado de ste es fijo y no variable. Por esa razn, para introducir los
aspectos ms concretos de la manipulacin de rboles, vamos a tratar con un tipo particular de
rboles de grado fijo, los llamados rboles binarios.

14.2. rboles binarios


Los rboles binarios constituyen un tipo particular de rboles de gran aplicacin. Estos rboles se
caracterizan porque no existen nodos con grado mayor que dos, es decir, un nodo tendr como
mximo dos subrboles.
Definicin: Un rbol binario es un conjunto finito de nodos que puede estar vaco o consistir
en un nodo raz y dos rboles binarios disjuntos, llamados subrbol izquierdo y
subrbol derecho.
En general, en un rbol no se distingue entre los subrboles de un nodo, mientras que en un rbol
binario se suele utilizar la nomenclatura subrbol izquierdo y derecho para identificar los dos
posibles subrboles de un nodo determinado. De forma que, por ejemplo, los dos siguientes rboles,
a pesar de contener la misma informacin son distintos por la disposicin de los subrboles:

Figura 3: Ejemplos de rboles.


Antes de pasar a la representacin de los rboles binarios, vamos a hacer algunas observaciones
relevantes acerca del nmero y caractersticas de los nodos en este tipo de rboles.
Lema 1: El nmero mximo de nodos en el nivel i de un rbol binario es 2i-1, i1, y el
nmero mximo de nodos en un rbol binario de altura k es 2k-1, k1.
Lema 2: Para cualquier rbol binario no vaco, si n0 es el nmero de nodos terminales y n2
es el nmero de nodos de grado 2, entonces se cumple que n0=n2+1. 1
Igualmente, para poder entender alguna de las formas de representacin de los rboles binarios,
vamos a introducir dos nuevos conceptos, lo que se entiende por rbol binario lleno y por rbol
binario completo.
Definicin: Se dice que un rbol binario est lleno si es un rbol binario de profundidad k
que tiene 2k-1 nodos.
Un rbol binario lleno es aquel que contiene el nmero mximo de posibles nodos. Como en estos
casos no existen subrboles vacos excepto para los nodos terminales, es posible realizar una
representacin secuencial eficiente de este tipo de rboles. Esta representacin suele implicar la
numeracin de los nodos. La numeracin se realiza por niveles y de izquierda a derecha. Este
proceso de numeracin (etiquetado) de los nodos permite una identificacin elegante de las

1
Demostraciones de estos lemas se pueden encontrar en Fundamentals of Data Structures in Pascal, Horowitz&Sahni,
pgs. 260,261

Tema 14. rboles 85


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
relaciones de parentesco entre los nodos del rbol y se ver ms adelante (en C/C++ suele empezar
la numeracin en 0 y terminar en n-1. En otros lenguajes, como Pascal, la numeracin suele
empezar en 1 y terminar en n. Nosotros nos ceiremos a la numeracin en C/C++.)
Definicin: Un rbol binario con n nodos y profundidad k se dice que es completo si y slo
si sus nodos se corresponden con los nodos numerados de 0 a n-1 en el rbol
binario lleno de profundidad k.
Cuando un rbol no es lleno pero es completo, tambin es posible la representacin secuencial
eficiente de la estructura de nodos que lo componen.

14.3. Fundamentos
Al igual que hemos visto en el resto de temas, empezaremos por introducir el tipo abstracto de datos
Arbol Binario.
Como en las otras estructuras de datos vistas, empezaremos viendo las operaciones que
consideraremos bsicas para la manipulacin de los rboles. Y al igual que hicimos en los otros
TAD tendremos diferentes tipos de operaciones:
De consulta sobre la informacin del TAD: En este caso, se deber poder consultar tanto la
informacin guardada en el nodo raz como los hijos del nodo raz (Informacion, HijoIzdo,
HijoDcho.)
De consulta sobre la propia estructura: Si la estructura contiene informacin, o por el contrario,
est vaca (ArbolVacio.)
El hecho de aadir y eliminar una cierta informacin del rbol, es un problema ciertamente
complejo, que depende de diferentes criterios y que es difcil de abordar desde el punto de vista
general. El problema de la eliminacin de informacin lo veremos de forma ms concreta ms
adelante.
La adicin de informacin, la veremos desde un punto de vista concreto en este momento, aunque
se puede ver de diferentes maneras (veremos otros puntos de vista ms adelante, en rboles binarios
de bsqueda y en montculos.)
Para aadir informacin: En este caso, la manera en que consideraremos que se puede aadir
informacin ser creando un nuevo rbol a partir de dos rboles ya existentes y con la
informacin que desamos aadir en el nodo raz, cada subrbol ser un hijo del nuevo rbol
(HacerArbol.)
Resumiendo, estas operaciones quedaran de la siguiente forma:
IniciarArbol ( ) ArbolBinario
ArbolVacio ( ArbolBinario ) Boolean
HacerArbol ( ArbolBinario, Valor, ArbolBinario ) ArbolBinario
HijoIzquierdo ( ArbolBinario ) ArbolBinario
HijoDerecho ( ArbolBinario ) ArbolBinario
Informacion ( ArbolBinario ) Valor

Y cumplen los siguientes axiomas:


p, r ArbolBinario d Valor
ArbolVacio ( IniciarArbol ( ) ) TRUE
ArbolVacio ( HacerArbol ( p, d, r ) ) FALSE
HijoIzquierdo ( HacerArbol ( p, d, r ) ) p

86 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
HijoIzquierdo ( IniciarArbol ( ) ) ERROR
HijoDerecho ( HacerArbol ( p, d, r ) ) r
HijoDerecho ( IniciarArbol ( ) ) ERROR
Informacion ( HacerArbol ( p, d, r ) ) d
Informacion ( IniciarArbol ( ) ) ERROR

A partir de esta informacin, ya somos capaces de codificar en C++ la interfaz de la clase rbol:

class Arbol
{
public:
typedef .??. Valor;

Arbol (void); /* IniciarArbol -> Constructor */


Arbol (const Arbol &); /* Constructor de copia */
~Arbol (void); /* Destructor */
const Arbol & Operator= (const Arbol &)

void HacerArbol (Arbol &, Valor, Arbol &);

bool ArbolVacio (void);


bool Informacion (Valor &);
Arbol &HijoIzdo (void);
Arbol &HijoDcho (void);

private:
...
};

Vista la parte pblica de la clase, ya podemos hacer uso de la misma.

14.4. Operaciones con rboles binarios: Recorrido de rboles binarios


Si se desea manipular la informacin contenida en un rbol, lo primero que hay que saber es cmo
se puede recorrer ese rbol, de manera que se acceda a todos los nodos del rbol solamente una vez.
El recorrido completo de un rbol produce un orden lineal en la informacin del rbol. Este orden
puede ser til en determinadas ocasiones.
Cuando se recorre un rbol se desea tratar cada nodo y cada subrbol de la misma manera. Existen
entonces seis posibles formas de recorrer un rbol binario:
(1) nodo - subrbol izquierdo - subrbol derecho
(2) subrbol izquierdo - nodo - subrbol derecho
(3) subrbol izquierdo - subrbol derecho - nodo
(4) nodo - subrbol derecho - subrbol izquierdo
(5) subrbol derecho - nodo - subrbol izquierdo
(6) subrbol derecho - subrbol izquierdo - nodo
Si se adopta el convenio de que, por razones de simetra, siempre se recorrer antes el subrbol
izquierdo que el derecho, entonces tenemos solamente tres tipos de recorrido de un rbol (los tres
primeros en la lista anterior). Estos recorridos, atendiendo a la posicin en que se procesa la
informacin del nodo, reciben, respectivamente, el nombre de recorrido prefijo, infijo y posfijo.

Tema 14. rboles 87


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
Los tres tipos de recorrido tienen una definicin muy simple si se hace uso de una expresin
recursiva de los mismos.

Algoritmo Prefijo
Entrada
Arbol arb
Inicio
si ( arb no es Vacio ) entonces
Procesar ( Informacion de arb )
Prefijo ( Hijo izquierdo de arb )
Prefijo ( Hijo derecho de arb )
fin_si
fin

Algoritmo Infijo
Entrada
Arbol arb
Inicio
Si ( arb no es Vacio ) entonces
Infijo ( Hijo izquierdo de arb )
Procesar ( Informacion de arb )
Infijo ( Hijo derecho de arb )
Fin_si
Fin

Algoritmo Posfijo
Entrada
Arbol arb
Inicio
Si ( arb no es Vacio ) entonces
Posfijo ( Hijo izquierdo de arb )
Posfijo ( Hijo derecho de arb )
Procesar ( Informacion de arb )
Fin_si
Fin

Ejemplo de recorrido de un rbol binario:
Recorrido Prefijo: ABCDEFG
Recorrido Infijo: CBDEAGF
Recorrido Posfijo: CEDBGFA

Figura 5

En C++, la implementacin de los recorridos es muy simple. Por ejemplo el recorrido Prefijo
quedara como sigue:

88 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I

Prefijo (const Arbol & arb)


{
Valor inf;

if (arb.ArbolVacio () == false)
{
arb.Informacion (inf);
Procesar (inf);

Prefijo (arb.HijoIzdo() );
Prefijo (arb.HijoDcho() );
}
}

El paso del parmetro lo hacemos por referencia, pero constante, para no tener que realizar la copia
de la informacin y hacer un mtodo ms eficiente.

14.5. Otras operaciones con rboles binarios


La primera operacin compleja a considerar sobre rboles binarios ser su generacin. En este caso,
no vamos a entrar a considerar todos los casos que pueden aparecer al insertar un nuevo nodo en un
rbol, la problemtica puede ser amplia y el proceso de generacin de un rbol depender de las
reglas impuestas por una aplicacin particular. Vamos a ver, sin embargo, algn ejemplo concreto
que permita ilustrar esta operacin.
Al manipular la estructura rbol, la situacin ms habitual es que el problema imponga una serie de
reglas para la construccin del rbol en funcin de los datos de entrada. Estos datos, en principio,
sern desconocidos antes de la ejecucin del programa. El procedimiento de generacin del rbol
deber, por tanto, reproducir de la manera ms eficiente posible esas reglas de generacin.

Ejemplo 1: Generacin de un rbol a partir de su recorrido prefijo.


Supongamos que se desea generar en memoria una estructura de rbol binario con unos
datos cuyas relaciones se conocen previamente. Es decir, el usuario va a trasladar al
ordenador una estructura de rbol conocida. Para hacer esto, se establece una notacin
secuencial que permita la interpretacin simple de las relaciones. La notacin consiste en
una secuencia de caracteres que indica la informacin de cada nodo en el rbol completo
asociado, de manera que se indica tambin si algn subrbol est vaco y se supone que la
informacin del subrbol izquierdo va siempre antes que la del subrbol derecho. En
realidad se trata de una representacin secuencial del recorrido prefijo del rbol. Se
considerar, para simplificar, que la informacin asociada con cada nodo es simplemente un
carcter y que los subrboles vacos se representan con un '.'. Entonces la entrada al
algoritmo de generacin puede ser simplemente una cadena de caracteres, donde cada uno
de ellos se interpreta como un nodo del rbol.
En este caso, las reglas de generacin del rbol binario a partir de su representacin
secuencial seran:
(1) Leer carcter a carcter la secuencia de entrada.
(a) Si el carcter que se lee es '.' no hay que crear ningn nodo, el subrbol est vaco.
(b) Si se lee un carcter distinto de '.' entonces se crea un nuevo nodo con la
informacin leda y se pasa a generar los subrboles izquierdo y derecho de ese
nodo, segn los mismos criterios y en ese mismo orden.

Tema 14. rboles 89


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
Representacin secuencial del rbol:
ABC..D.E..FG...

Figura 6

Un algoritmo que implemente estas reglas de generacin utilizando exclusivamente las
operaciones bsicas podra ser:

Algoritmo Generar_Arbol
Salida
ArbolBinario arb
Auxiliar
ArbolBinario izq, der
Inicio
Leer ( carcter )
Si(1) carcter '.' entonces
izq Generar_Arbol
der Generar_Arbol
arb HacerArbol (izq, caracter, der)
Sino(1)
arb IniciarArbol ( )
Fin_si(1)
Fin

Se puede observar como la recursividad permite definir de una manera simple y elegante el
proceso de generacin.
Esta funcin escrita en C++ quedara como sigue:

void GenerarArbol (Arbol & arb)


{
Arbol izq, der;

cin >> x

if (x != .)
{
GenerarArbol (izq);
GenerarArbol (der);
arb.HacerArbol (izq, x, der);
}
}


Resaltar, que en ningn momento se llama a ninguna funcin para iniciar el rbol, ya que
por el hecho de declararlo el rbol es creado vaco (se llama al constructor por defecto de la
clase).

90 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I

Ejemplo 2: rbol binario para la representacin de una expresin algebraica.


Generar el rbol binario de representacin de una expresin algebraica. Para ello se realizan
las siguientes consideraciones:
(1) La entrada del programa deber ser una expresin escrita en notacin postfija
guardada en una pila.
Por ejemplo, la expresin: 2 3 + 4 * (que equivaldra a (2+3)*4) estara guardada
en una pila de la siguiente manera:
Cima *
4
+
3
2

(2) En las expresiones slo se tendrn en cuenta los cuatro operadores binarios
bsicos: +, -, *, /.
(3) Los operandos sern exclusivamente caracteres (1 carcter, 1 operando).
A partir de estas consideraciones, la generacin del rbol de la expresin quedara:

Algoritmo Crear_Arbol

Entradas
pila_expr: Pila
Salidas
arb: ArbolBinario
Variables
der, izq: ArbolBinario
Inicio
Si(1) ( no Vacia_Pila ( pila_expr ) ) entonces
y Desapilar ( pila_expr )
Si(2) ( y es operador ) entonces
{* como se lee la expresin al revs, el primer operando que se lee *}
{* es en realidad el ltimo, por eso se genera antes el rbol derecho *}
Crear_Arbol ( der )
Crear_Arbol ( izq )
Sino(2)
der IniciarArbol ( )
izq IniciarArbol ( )
Fin_si(2)
arb HacerArbol ( izq, y, der )
Fin_si(1)
Fin

Ejemplo 3: Copia de un rbol binario en otro.


Como un ejemplo ms de manipulacin de un rbol, veamos un algoritmo que nos permita
realizar un duplicado de un rbol ya existente (sin utilizar el constructor de copia). Al copiar
el rbol se debe copiar toda la informacin contenida en el mismo, con la misma estructura
de relaciones que existe en el rbol original.
El proceso de copia implica el recorrido sistemtico del rbol original, al mismo tiempo que
se genera un nuevo rbol con la misma informacin.

Tema 14. rboles 91


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
Esta funcin escrita en C++ quedara como sigue:

void CopiarArbol (Arbol & des, Arbol ori)


{
Arbol izq, der;
Valor x;
bool b_aux;

if (!ori.ArbolVacio () )
{
CopiarArbol (izq, ori.HijoIzdo () );
CopiarArbol (der, ori.HijoDcho () );
b_aux = ori.Informacion (x);
des.HacerArbol (izq, x, der);
}
}

Ejemplo 4: Equivalencia entre rboles binarios.


Otro problema que se puede solucionar fcilmente utilizando la recursin es el de determinar
la equivalencia entre dos rboles binarios. Se dice que dos rboles binarios son equivalentes
si presentan la misma topologa (la misma estructura de relaciones entre nodos) y la
informacin en los nodos correspondientes es la misma.
La funcin, escrita en C++, siguiendo la interfaz de la clase propuesta quedara como sigue:

bool Equivalente (Arbol arb1, Arbol arb2)


{
bool b_aux, equiv = false;

if (arb1.ArbolVacio () && arb2.ArbolVacio () )


equiv = true;
else
{
if (!arb1.ArbolVacio () && !arb2.ArbolVacio () )
{
b_aux = arb1.Informacion (x);
b_aux = arb2.Informacion (y);
if (x == y)
if (Equivalente (arb1.HijoIzdo (), arb2.HijoIzdo() )
equiv = Equivalente (arb1.HijoDcho (), arb2.HijoDcho() );
}
}
return equiv;
}

14.6. Representacin de los rboles binarios

Representacin mediante arrays


Como hemos visto, si el rbol binario que se desea representar cumple las condiciones de rbol
lleno o rbol completo, es posible encontrar una buena representacin secuencial del mismo. En
esos casos, los nodos pueden ser almacenados en un array unidimensional, A, de manera que el
nodo numerado como i se almacena en A[i]. Esto permite localizar fcilmente las posiciones de
los nodos padre, hijo izquierdo e hijo derecho de cualquier nodo i en un rbol binario arbitrario que
cumpla tales condiciones.
92 Ricardo Ferrs / Jess Albert
Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
Lema 3: Si un rbol binario completo con n nodos se representa secuencialmente, se cumple
que para cualquier nodo con ndice i, 0i(n-1), se tiene que:
(1) El padre del nodo i estar localizado en la posicin (i-1)/2 si i>0. Si i=0, se
trata del nodo raz y no tiene padre.
(2) El hijo izquierdo del nodo i estar localizado en la posicin (2*(i+1))-1 si
2*(i+1)-1<n. Si 2*(i+1)-1n, el nodo no tiene hijo izquierdo.
(3) El hijo derecho del nodo i estar localizado en la posicin (2*(i+1)) si
2*(i+1)<n. Si 2*(i+1)n, el nodo no tiene hijo derecho.
Evidentemente, la representacin puramente secuencial de un rbol se podra extender
inmediatamente a cualquier rbol binario, pero esto implicara, en la mayora de los casos,
desaprovechar gran cantidad del espacio reservado en memoria para el array. As, aunque para
rboles binarios completos la representacin es ideal y no se desaprovecha espacio, en el peor de los
casos, para un rbol lineal (una lista) de profundidad k, se necesitara espacio para representar
2k-1 nodos, y slo k de esas posiciones estaran realmente ocupadas.
Adems de los problemas de eficiencia desde el punto de vista del almacenamiento en memoria,
hay que tener en cuenta los problemas generales de manipulacin de estructuras secuenciales. Las
operaciones de insercin y borrado de elementos en cualquier posicin del array implican
necesariamente el movimiento potencial de muchos nodos. Estos problemas se puede solventar
adecuadamente mediante la utilizacin de una representacin enlazada de los nodos.

Representacin mediante punteros


La mejor solucin, de nuevo, para evitar los problemas asociados con la manipulacin de arrays, es
la representacin de los rboles binarios mediante estructuras dinmicas, en el sentido de la
creacin en tiempo de ejecucin de los nodos que sean necesarios (no ms) y la utilizacin de
punteros para enlazar estos nodos.
La estructura de cada nodo en esta representacin estar compuesta por tres campos en los que se
guardar la informacin y enlaces a los subrboles que dependen de cada nodo.

Tipos:
Arbol = Puntero a NodoArbol;

NodoArbol = registro
Info : Valor;
Izq, Der: Arbol;
fin;

Se puede comprobar como la representacin en memoria de la estructura, en cuanto a definicin de


tipos de datos, coincide exactamente con la de, por ejemplo, una lista doblemente ligada, sin que
ello implique que se est hablando de la misma estructura de datos. De nuevo hay que dejar muy
claro que es la interpretacin que hacemos de la representacin en memoria la que define una
estructura de datos (definicin de operaciones), no la representacin en s misma.
La representacin enlazada tiene como principal desventaja que no resulta inmediato la
determinacin del padre de un nodo. Es necesario buscar el camino de acceso al nodo dentro del
rbol para poder obtener esa informacin. Sin embargo, en la mayora de las aplicaciones es la
representacin ms adecuada. En el caso en que la determinacin del padre de un nodo sea una
operacin importante y frecuente en una aplicacin concreta, se puede modificar ligeramente la
estructura de cada nodo aadiendo un cuarto campo, tambin de tipo Puntero a NodoArbol, que
guarde la posicin del padre de cada nodo.

Tema 14. rboles 93


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
Una posible representacin, con clases de C++, basndonos en estas ideas podra ser la siguiente:

class Arbol
{
public:
...
private:
typedef Arbol * PunteroArbol;

bool esvacio;

Valor info;
PunteroArbol izdo;
PunteroArbol dcho;
};

Con esta representacin, la codificacin de las operaciones de la interfaz de la clase en C++ sera la
siguiente:

Constructores de la clase
El constructor por defecto se limitar a poner el rbol como vaco:

Arbol::Arbol (void)
{
esvacio = true;
}

El constructor de copia se puede hacer bsicamente utilizando la misma idea que la funcin de
copia entre rboles, pero implementando la funcin de copia de rboles como un mtodo privado de
la clase, y utilizndola en el constructor de copia. La funcin CopiarArbol, la podremos reutilizar en
otras operaciones de copia de rboles.

void Arbol::Copiar (const Arbol & ori)


{
esvacio = ori.esvacio;
if (!ori.esvacio)
{
info = ori.info;
izdo = new Arbol;
izdo->CopiarArbol (*ori.izdo);
dcho = new Arbol;
dcho->CopiarArbol (*ori.dcho);
}
}

Utilizando este mtodo el constructor de copia quedara como sigue:

void Arbol:: Arbol (const Arbol & ori)


{
this->Copiar (ori);
}

94 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
Otra manera de implementar el constructor de copia sera utilizando la idea de que la llamada al new
realiza la llamada implcita al constructor de la clase, si el new lo utilizamos con clases.
Cuando invocamos al new, se reserva espacio para el nuevo elemento del tipo del operando con que
se invoca al new. Si ste es una clase, se llama de forma automtica al constructor adecuado, en
principio si hacemos new Arbol se llamar al constructor por defecto (el que no tiene parmetros).
Sin embargo si hacemos llamada con la clase con parmetros, se buscar entre los constructores
aqul que tenga el parmetro adecuado. Si hacemos la llamada con new Arbol (arb), el
constructor que acepta como parmetro un rbol es el constructor de copia, de manera que se
reservar espacio para el nuevo elemento y se llamar al constructor de copia con el parmetro
arb. Con esto ya conseguimos hacer la reserva recursiva de todos los nodos del rbol

Arbol::Arbol (const Arbol & arb)


{
if (arb.esvacio == true)
esvacio = true;
else
{
esvacio = false;
info = arb.info;
izdo = new Arbol(*arb.izdo);
dcho = new Arbol(*arb.dcho);
}
}

Sobrecarga del operador =


Como en el resto de clases representadas mediante punteros, la simple asignacin de los objetos
hace que se comparta la memoria dinmica de los mismo, con los consecuentes problemas en la
manipulacin de los objetos.
Por ello, al igual que hicimos en las otras clases, incluiremos como mtodo la sobrecarga del
operador =.
Aprovecharemos para ello el mtodo Copiar ya desarrollado y previo a la copia vaciaremos la
estructura mediante un nuevo mtodo privado Vaciar.

const Arbol & Arbol::operator= (const Arbol & orig)


{
Arbol tmp;

tmp.Copiar(orig);

Vaciar();
Copiar (tmp);

return *this;
}


La copia previa en el temporal se realiza para evitar problemas en asignaciones incrementales,
que repitan operandos en la parte derecha e izquierda de la asignacin.
Habr que implementar el mtodo privado vaciar.
Este mtodo se limitar a liberar todos y cada uno de los nodos del rbol, empezando desde las
hojas y terminando en el nodo raz, es decir, siguiendo un recorrido postfijo del rbol.
Tema 14. rboles 95
Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
As el mtodo vaciar quedara como sigue:

void Arbol::Vaciar ()
{
if (!esvacio)
{
delete (izdo);
delete (dcho);
}
esvacio = true;
}

Mtodo HacerArbol
El mtodo HacerArbol, al igual que el constructor de copia, puede realizarse utilizando el mtodo
de copia de rboles, o basarse en la utilizacin del new, que llamar al constructor de copia y que
bsicamente copia rboles:

void Arbol::HacerArbol (Arbol & izq, Valor inf, Arbol & der)
{
PunteroArbol tmp_izq, tmp_der;

tmp_izq = new Arbol (izq);


tmp_der = new Arbol (der);

Vaciar ();
esvacio = false;
info = inf;

izdo = tmp_izq;
dcho = tmp_der;

return;
}

Mtodos de consulta
Los mtodos de consulta, tanto sobre el propio rbol, como sobre el contenido del rbol, sern
relativamente sencillos:

bool Arbol::ArbolVacio (void)


{
return esvacio;
}

bool Arbol::Informacion (Valor & inf)


{
bool exito;

if (esvacio == true)
exito = false;
else
{
exito = true;

96 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I

inf = info;
}
return exito;
}

Arbol & Arbol::HijoIzdo (void)


{
return *izdo;
}

Arbol & Arbol::HijoDcho (void)


{
return *dcho;
}

Destructor de la clase
Una vez realizado el mtodo Vaciar podemos utilizarlo para implementar el destructor de la clase:

Arbol::~Arbol ()
{
Vaciar();
}

Eliminacin de nodos en un rbol binario


La eliminacin de nodos es otra operacin habitual de manipulacin de rboles binarios. Hay que
tener en cuenta que esta operacin debe mantener la estructura del rbol y, por tanto, depender del
orden que se haya establecido dentro del rbol (si lo hay). Esto hace que la eliminacin de nodos se
pueda convertir en una operacin ms compleja y que no se pueda hablar de un algoritmo para
borrar en general, ms bien se podr hablar de algoritmos para borrar nodos para un determinado
orden dentro del rbol.
Si por ejemplo, un rbol ha sido generado teniendo en cuenta que sus nodos est correctamente
ordenados si se recorre el rbol en orden infijo, se debe considerar un algoritmo de borrado que no
altere este orden. La secuencia de orden a que da lugar el recorrido infijo se debe mantener al borrar
cualquier nodo del rbol.
Existen dos casos a considerar cuando se borra un nodo del rbol. El primer caso, el ms simple,
implica borrar un nodo que tenga al menos un subrbol vaco. El segundo caso, ms complejo,
aparece cuando se desea borrar un nodo cuyos dos subrboles son no vacos. En este ltimo caso,
hay que tener que el nodo borrado debe ser sustituido por otro nodo, de manera que se mantenga la
estructura inicial del rbol. Cuando el nodo a borrar posee dos subrboles no vacos, el proceso de
reestructuracin de las relaciones dentro del rbol resulta ms complejo, ya que cuando el nodo es
terminal, eliminar ese nodo es tan simple como eliminar el enlace que le mantiene unido al rbol
desde su nodo padre, y si el nodo posee un nico subrbol no vaco, el nodo debe ser sustituido por
su nodo hijo. Estas dos ltimas situaciones no implican una reestructuracin importante del rbol.
Suponiendo que el rbol debe mantener el orden establecido entre los nodos por un recorrido
particular del mismo, el problema es determinar qu nodo debe sustituir al nodo que se va a borrar.
Si volvemos al ejemplo anterior, el orden establecido por el recorrido infijo del rbol sera:
Recorrido infijo: CBDEAGF
Tema 14. rboles 97
Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
Si se desea borrar el nodo B, que tiene dos nodos hijos, el nodo que debera sustituirle sera el nodo
D, ya que es su sucesor en el orden establecido por el recorrido infijo (criterio escogido en este
ejemplo particular). En general, sea cul sea el orden establecido entre los nodos, el sustituto de un
nodo que se va a borrar deber ser su sucesor en dicho orden. De esta manera se mantiene la
estructura global del rbol.
Los diferentes criterios de ordenacin de los nodos van a dar lugar a distintos algoritmos para
determinar el sucesor de un nodo, segn el orden establecido, y para modificar los enlaces entre
nodos.
Ejemplo
Veamos el ejemplo concreto de un algoritmo que permita borrar un nodo en el caso en que
se supone como vlido el orden establecido entre los nodos por un recorrido infijo del rbol:

Algoritmo BorrarNodo

Entrada
ArbolBinario arb { Raiz del rbol de donde se pretende borrar }
ArbolBinario donde { rbol a borrar }
Variables
ArbolBinario pad, suc

Inicio
Si(1) (donde no es vaco) entonces
{ Subrbol izquierdo vaco (o nodo terminal) }
Si(2) (Hijo izquierdo de donde es vaco) entonces
Si(3) (donde = arb) entonces
arb Hijo derecho de donde
Sino(3)
pad Padre (donde, arb)
Si(4) ( Hijo izquierdo de pad = donde ) entonces
Hijo izquierdo de pad Hijo derecho de donde
Sino(4)
Hijo derecho de pad Hijo derecho de donde
Fin_si(4)
Fin_si(3)
Liberar_Espacio (donde)
Sino(2)
{Subrbol derecho vaco}
Si(5) (Hijo derecho de donde es vaco) entonces
Si(6) ( donde = arb ) entonces
arb Hijo izquierdo de donde
Sino(6)
pad padre (donde, arb)
Si(7) (Hijo izquierdo de pad = donde ) entonces
Hijo izquierdo de pad Hijo izquierdo de donde
Sino(7)
Hijo derecho de pad hijo izquierdo de donde
Fin_si(7)
Fin_si(6)
Liberar_Espacio ( donde )
Sino(5)
{ Subrboles NO vacos }
{ El nodo raiz no es un caso especial }
suc Sucesor_Infijo (donde)
Informacion de donde Informacion de suc
{ Se puede utilizar la recursin : }

98 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
{ BorrarNodo ( arb, suc ) }
{ O borrarlo directamente: }
pad padre (suc, arb)
Si(8) (donde = pad) entonces
Hijo derecho de pad Hijo derecho de suc
Sino(8)
Hijo izquierdo de pad Hijo derecho de suc
Fin_si(8)
Liberar_Espacio (suc)
Fin_si(5)
Fin_si(2)
Fin_si(1)
Fin

Hace falta especificar los algoritmos que permitirn encontrar el padre (Padre) y el sucesor
infijo de un nodo del rbol (Sucesor_Infijo).

Algoritmo Padre

Entradas
ArbolBinario donde, arb { Se pretende buscar el subrbol donde en el subrbol arb}
Salida
ArbolBinario
Variable
ArbolBinario aux

Inicio
Si(1) (arb no es vaco) entonces
Si(2) (Hijo izquierdo de arb = donde ) o (Hijo derecho de arb = donde) entonces
Devolver (arb)
Sino(2)
aux Padre (donde, Hijo izquierdo de arb)
Si(3) (aux es vaco) entonces
aux Padre (donde, Hijo derecho de arb)
Fin_si(3)
Devolver ( aux )
Fin_si(2)
Sino(1)
Devolver (Arbol vaco)
Fin_si(1)
Fin

El algoritmo que busca el padre de un nodo es en realidad un algoritmo general de bsqueda,


que permite recorrer todo el rbol hasta encontrar un nodo que cumpla la condicin
establecida, independientemente del orden que pudiera existir en el rbol. Por lo tanto,
puede ser fcilmente modificado para poder localizar un nodo a partir de la informacin que
contiene. En este caso, el algoritmo general de bsqueda de un nodo por su informacin
sera:

Algoritmo Buscar

Entradas
Valor x

Tema 14. rboles 99


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
ArbolBinario arb
Salida
ArbolBinario
Variable
aux: ArbolBinario

Inicio
Si(1) (arb no es vaco) entonces
Si(2) (Informacin de arb = x ) entonces
Devolver (arb)
Sino(2)
aux Buscar (x, Hijo izquierdo de arb)
Si(3) (aux es vaco) entonces
aux Buscar (x, Hijo derecho de arb)
Fin_si(3)
Devolver (aux)
Fin_si(2)
Sino(1)
Devolver (Arbol vaco)
Fin_si(1)
Fin

Volviendo al algoritmo de borrado, hemos visto que es necesario localizar el sucesor infijo
de un nodo. En este caso el algoritmo es muy simple. Se tratara de recorrer el subrbol
derecho del nodo, siempre a travs de los enlaces izquierdos, hasta encontrar un nodo que
tenga el subrbol izquierdo vaco. Este algoritmo se puede representar fcilmente tanto
utilizando un esquema recursivo como iterativo. A continuacin se muestran los dos
algoritmos.

Algoritmo Sucesor_Infijo_Recursivo

Entrada
ArbolBinario arb
{ En este caso el primer valor de entrada del algoritmo debe ser el subrbol derecho }
{ del nodo del cual se desea encontrar el sucesor. La llamada deberia hacerse como: }
{ Sucesor_Infijo_Recursivo (Hijo derecho de arb ) }
Salida
ArbolBinario

Inicio
Si (Hijo izquierdo de arb no es un rbol vaco) entonces
Devolver (Sucesor (hijo izquierdo de arb) )
Sino
Devolver (arb)
Fin_si
Fin

Algoritmo Sucesor_Infijo_Iterativo

Entrada
ArbolBinario arb
Salida
PunteroArbol

100 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
Inicio
arb Hijo derecho de arb
Mientras (Hijo izquierdo de arb no sea vaco) hacer
arb arb^.izq
Fin_mientras
Devolver ( arb )
Fin

14.7. Representacin de rboles generales como rboles binarios


Vamos a ver en este apartado que cualquier rbol se puede representar como un rbol binario. Esto
es importante porque resulta ms complejo manipular nodos de grado variable (nmero variable de
relaciones) que nodos de grado fijo. Una posibilidad evidente de fijar un lmite en el nmero de
relaciones sera seleccionar un nmero k de hijos para cada nodo, donde k es el grado mximo
para un nodo del rbol. Sin embargo, esta solucin desaprovecha mucho espacio de memoria.
Lema 4: Para un rbol k-ario (es decir, grado k) con n nodos, cada uno de tamao fijo, el
nmero de enlaces nulos en el rbol es n*(k-1)+1 de los n*k campos de tipo enlace
existentes, n1.
Esto implica que para un rbol de grado 3, ms de 2/3 de los enlaces sern nulos. Y la proporcin de
enlaces nulos se aproxima a 1 a medida que el grado del rbol aumenta. La importancia de poder
utilizar rboles binarios para representar rboles generales reside en el hecho de que slo la mitad
de los enlaces son nulos, adems de facilitar su manipulacin.
Para representar cualquier rbol por un rbol binario, vamos a hacer uso implcito de que el orden
de los hijos de un nodo en un rbol general no es importante.

Figura 8 Figura 9
La razn por la cual, para representar un rbol general, se necesitaran nodos con muchos enlaces es
que, hasta ahora, hemos pensado en una representacin basada en la relacin padre-hijo dentro del
rbol, y un nodo pueden tener un gran nmero de hijos. Sin embargo, se puede encontrar una forma
de representacin donde cada nodo slo necesite tener dos relaciones, una que lo una con el hijo que
tenga ms a la izquierda y otra que lo una con el siguiente nodo hermano por la derecha.
Estrictamente hablando, como el orden de los hijos en un rbol no es importante, cualquiera de los
hijos de un nodo podra ser el hijo que est ms a la izquierda el nodo padre y cualquiera de los
nodos hermanos podra ser el siguiente hermano por la derecha. Por lo tanto, la eleccin de estos
nodos no dejar de ser, hasta cierto punto, arbitraria. Podemos basarnos para esta eleccin en la
representacin grfica del rbol que se desea almacenar. Veamos el siguiente ejemplo, el rbol
binario correspondiente al rbol de la figura se obtiene conectando juntos todos los hermanos de un

Tema 14. rboles 101


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
nodo y eliminando los enlaces de un nodo con sus hijos, excepto el enlace con el hijo que tiene ms
a la izquierda.
Este tipo de representacin se puede identificar con los rboles binarios que ya hemos visto,
asociando el enlace izquierdo del rbol con el enlace al hijo de la izquierda y el enlace derecho con
el enlace al nodo hermano. Se observa que de esta manera el nodo raz nunca tendr un subrbol
derecho, ya que no tiene ningn nodo hermano. Pero esto nos permite representar un bosque de
rboles generales como un nico rbol binario, obteniendo primero la transformacin binaria de
cada rbol y despus uniendo todos los rboles binarios considerando que todos los nodos raz son
hermanos. Ver el ejemplo de la figura.

Figura 10
En todas estas transformaciones de rbol general a rbol binario hay que tener en cuenta la
interpretacin que se deben hacer de las relaciones, no es lo mismo que un nodo est a la izquierda
o a la derecha de otro, ya que los enlaces izquierdos unen un nodo con su hijo, mientras que los
enlaces derechos unen dos nodos hermanos. De cualquier forma, teniendo en cuenta este aspecto, es
posible aplicar los algoritmos de manipulacin de rboles binarios que se han visto hasta ahora. De
hecho, en todos estos algoritmos se diferenciaba claramente entre el tratamiento del subbol
izquierdo y el del subrbol derecho.

14.8. rboles binarios de bsqueda


Los rboles binarios de bsqueda son estructuras de datos que presentan un gran rendimiento
cuando las funciones a realizar implican bsquedas, inserciones y eliminacin de nodos. De hecho,
con un rbol de bsqueda, dichas operaciones se pueden realizar tanto a partir de un valor clave,
como por un valor ordinal (es decir, encontrar un elemento con clave x, encontrar el sexto elemento
ms pequeo, borrar el elemento con clave x, borrar el sexto elemento ms pequeo, insertar un
elemento y determinar su ordinal dentro del rbol).
Definicin: Un rbol binario de bsqueda es un rbol binario, que puede estar vaco, y que si
es no vaco cumple las siguientes propiedades:
(1) Todos los nodos estn identificados por una clave y no existen dos elementos con
la misma clave.
(2) Las claves de los nodos del subrbol izquierdo son menores que la clave del nodo
raz.
(3) Las claves de los nodos del subrbol derecho son mayores que la clave del nodo
raz.

102 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
(4) Los subrboles izquierdo y derecho son tambin rboles binarios de bsqueda.
La primera propiedad resultara redundante, ya que de las propiedades (2), (3) y (4) se puede
deducir que la clave de un nodo es nica. Sin embargo, la aparicin explcita de esta propiedad hace
que la definicin sea ms clara.

Operaciones en rboles binarios de bsqueda


Veamos ahora cmo manipular este tipo especial de rboles binarios.
En este tipo de rboles tendremos como operaciones fundamentales las operaciones de bsqueda,
insercin y borrado de elementos.
Con estas operaciones y las bsicas de rboles generales la interfaz de la clase nos quedara:
class ABB //Arbol Binario de Busqueda
{
public:
typedef .??. Valor;

ABB();
ABB (const ABB &);
~ABB();

bool ABBVacio() ;
bool Informacion ( Valor& ) ;
ABB& HijoIzq() ;
ABB& HijoDer();

void Asignar ( ABB& );


void Borrar ();
ABB& Buscar ( Valor );
bool Insertar ( Valor );
bool Eliminar ( Valor );

private:
typedef ABB * Puntero;

bool esvacio;
Valor info;
Puntero izdo, dcho;

void Copiar (ABB &);


};

Nos centraremos en las operaciones especiales de rboles binarios de bsqueda, ya que el resto de
las operaciones son las mismas que en los rboles binarios generales.

Bsqueda
La definicin de rbol binario de bsqueda especifica un criterio en la estructuracin del rbol en
funcin de las claves de los nodos. En este caso, existe un criterio de ordenacin de los nodos. Por
lo tanto, ser bastante simple describir un mtodo eficiente de bsqueda que explote esa ordenacin.
Suponer que se busca un elemento que posea una clave x. La bsqueda comenzar por el nodo raz
del rbol. La clave de ese nodo informar por dnde debe continuar la bsqueda, ya no es necesario
recorrer exhaustivamente todos los nodos del rbol. Si la clave del nodo es igual a x, la bsqueda
finaliza con xito. Si la clave es menor que x, se sabe que si existe un nodo en el rbol que posea
como clave el valor x deber estar en el subrbol derecho, por lo tanto la bsqueda deber continuar
por esa parte del rbol. Si, por el contrario, la clave del nodo es mayor que x, entonces la bqueda
deber continuar por el subrbol izquierdo. El proceso continuar hasta que se encuentre un nodo

Tema 14. rboles 103


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
con clave igual a x o un subrbol vaco, en cuyo caso se puede asegurar que no existe ningn nodo
con clave x en el rbol. Este mtodo de bsqueda sugiere seguir un esquema recursivo.
Suponiendo que la clave que identifica cada nodo es un campo contenido en la informacin general
del nodo, el algoritmo de bsqueda quedara como sigue:

Arbol & Arbol::BuscarRecurrenteABB (Valor x)


{
PunteroArbol p_aux;

if (esvacio == true)
p_aux = this;
else
if (info == x)
p_aux = this;
else
if (x < info)
p_aux = &(izdo.BuscarRecurrenteABB (x) );
else
p_aux = &(dcho.BuscarRecurrenteABB (x) );

return (*p_aux);
}

En este ejemplo, la recursividad puede ser fcilmente susituida por un esquema iterativo mediante
la utilizacin de un bucle de repeticin mientras. En ese caso, el algoritmo de bsqueda iterativo
sera:

Arbol & Arbol::BuscarIterativoABB (Valor x)


{
PunteroArbol p_aux;

p_aux = this;

while ( (p_aux->esvacio != true) && (p_aux->info != x) )


{
if (x < p_aux->info)
p_aux = p_aux->izdo;
else
p_aux = p_aux->dcho;
}

return (*p_aux);
}

El proceso de bsqueda en un rbol binario de bsqueda resulta muy eficaz. El coste asociado sera
O(h), donde h es la altura del rbol. Hay que hacer notar que la dependencia es lineal con la altura
del rbol y no con el nmero de elementos del mismo. En funcin del nmero de nodos en el rbol,
n, el coste sera O(log n) (Esto es cierto si el rbol est suficientemente equilibrado. En el peor de
los casos el rbol binario de bsqueda degenerara en una lista y el coste llegara a ser lineal.)
El mtodo de bsqueda se asemeja mucho al mtodo de bsqueda binaria sobre arrays ordenados,
tras la comparacin con un elemento se puede decidir en qu regin de la estructura se puede
encontrar la informacin buscada, descartndose el resto de los elementos de la estructura. De esta

104 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
forma, se reduce considerablemente el nmero de comparaciones necesarias para localizar el
elemento.

Insercin
La insercin de un nuevo nodo en un rbol binario de bsqueda debe realizarse de tal forma que se
mantengan las propiedades del rbol. De modo que lo primero que hay que hacer es comprobar que
en el rbol no existe ningn nodo con clave igual a la del elemento que se desea insertar. Si la
bsqueda de dicha clave falla, entonces se insertar el nuevo nodo en el punto donde se ha parado la
bsqueda, que ser el punto del rbol donde, de existir, debera estar ubicada dicha clave. Hay que
considerar que todo proceso de bsqueda fallido termina en un enlace nulo, por tanto, el nodo a
insertar siempre ser un nodo terminal, lo que facilitar la operacin.

Insertar 19


Figura 11
El algoritmo que implementa la estrategia de insercin es el siguiente:

bool Arbol::InsertarIterativo (Valor x)


{
PunteroArbol donde;
bool enc = false, exito = false;

while ( (donde->esvacio != true) && (!enc) )


{
if (x == donde->info)
enc = true;
else
if (x < donde->info)
donde = donde->izdo;
else
donde = donde->dcho;
}

if (!enc)
{
exito = true;
donde->esvacio = false;
donde->izdo = new Arbol;
donde->dcho = new Arbol;
donde->info = x;
}
}

Esta funcin se podra implementar fcilmente de forma recursiva de la siguiente manera:


bool Arbol::InsertarRecurrente (Valor x)


{
bool exito = false;

Tema 14. rboles 105


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
if (esvacio == true)
{
xito = true;

esvacio = false;
izdo = new Arbol;
dcho = new Arbol;
info = x;
}
else
if (x != info)
if (x < info)
exito = izdo->InsertarRecurrente (x);
else
exito = dcho->InsertarRecurrente (x);

return exito;
}


Si nos fijamos, la primera parte de la insercin es la bsqueda del lugar en dnde queremos insertar.
Podemos utilizar el mtodo de bsqueda para evitar esa parte. Utilizando el mtodo de bsqueda
(nos dara igual el mtodo iterativo o el recursivo para hacerlo) nos quedara la siguiente insercin:

bool Arbol::Insertar (Valor x)


{
bool exito = false;
Arbol & donde = Buscar (x);

if (donde->esvacio)
{
exito = true;
donde->esvacio = false;
donde->izdo = new Arbol;
donde->dcho = new Arbol;
donde->info = x;
}

return exito;
}


La operacin de insercin de un nodo se puede hacer en un tiempo O(h), donde 'h' es la altura del
rbol de bsqueda.

Borrado
La operacin de borrado en un rbol de bsqueda es muy simple. Hay que tener en cuenta que en
todo rbol de bsqueda los nodos se pueden ordenar por su clave si se recorre el rbol siguiendo el
criterio infijo. El orden establecido por este recorrido se debe mantener aunque se elimine un nodo
del rbol.
Podemos establecer como caso bsico la eliminacin de un nodo que no tenga hijos. Si ese es el
caso la eliminacin de la informacin es inmediata (se marca el nodo como vaco y se libera el
espacio de sus hijos vacios).
Si no es este el caso se busca su sucesor en el rbol (que estar a la izquierda del rbol que es hijo
derecho del nodo a borrar y se sustituye la informacin de ese nodo por la que pretendemos borrar y
se llama de nuevo a borrar con ese nodo. Si no existiese un sucesor, existira un antecesor y
realizariamos la misma operacin.
106 Ricardo Ferrs / Jess Albert
Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
La implementacin en C++ de este cdigo, sera la siguiente:

bool ABB::Eliminar (Valor x)


{
Puntero p_aux, p_tmp;
bool exito;

p_aux = & (this->Buscar (x) );

if (p_aux->esvacio)
exito = false;
else
{
//Intento sustituir por el menor en p_aux->dcho
p_tmp = p_aux->dcho;
if ( !p_tmp->esvacio)
{
while ( !p_tmp->izdo->esvacio)
p_tmp = p_tmp->izdo;
p_aux->info = p_tmp->info;
exito = p_tmp->Eliminar(p_tmp->info);
}
else
{
//Intento sustituir por el mayor en aux->izq
p_tmp = p_aux->izdo;
if ( !p_tmp->esvacio)
{
while ( !p_tmp->dcho->esvacio)
p_tmp = p_tmp->dcho;
p_aux->info = p_tmp->info;
exito = p_tmp->Eliminar(p_tmp->info);
}
else //Nodo terminal
{
p_aux->esvacio = true;
delete p_aux->izdo;
delete p_aux->dcho;
exito = true;
}
}
}

return exito;
}

Ejemplo de aplicacin de rboles binarios de bsqueda


Contar el nmero de veces que aparecen las palabras que forman un texto.

Algoritmo:
- Leer el texto palabra a palabra.
- Almacenar las palabras leidas en forma de rbol binario de bsqueda con un campo
asociado que indique el nnero de veces qe aparece cada una.
- Cuando se lee una nueva palabra, comprobar si est o no en el rbol, lo que indica
si ya haba aparecido anteriormente o no. Si la palabra est en el rbol aumentar su
contador de apariciones en 1, sino insertarla en el rbol e iniciar el contador a 1.

Tema 14. rboles 107


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
- Cuando se alcance el final del texto se tendr toda la informacin de inters en
forma de rbol de bsqueda, si se recorre este rbol en orden infijo se podr
obtener un listado en orden alfabtico de las palabras con la frecuencia de
aparicin de cada palabra.

14.9. Heaps (montculos)


Veamos otro tipo especial de rbol binario, los llamados heaps (motculos), que se pueden
representar eficazmente con un vector.
Definicin: Un heap de mximos (mnimos), max heap (min heap), es un rbol binario
completo tal que, el valor de la clave de cada nodo es mayor (menor) o igual que las
claves de sus nodos hijos (si los tiene).

Montculo de mximos Montculo de mnimos


max heap min heap
Figura 12
De la definicin se deduce que la clave contenida en el nodo raz de un max heap (min heap) es la
mayor (menor) clave del rbol. Pero esto no quiere decir que sea la nica con ese valor.
La estructura heap tiene interesantes aplicaciones, por ejemplo, la ordenacin de arrays (algoritmo
heapsort) o el mantenimiento de las llamadas colas de prioridad. Las colas de prioridad son un tipo
especial de colas donde todo elemento que se inserta en la cola lleva asociado una prioridad, de
forma que el elemento que se borra de la cola es el primero entre los que tengan la mxima (o
mnima) prioridad.
Los heaps, como rboles binarios completos que son, se pueden representar de forma eficaz
mediante una estructura secuencial (un array). A cada nodo del rbol se le asigna una posicin
dentro de la secuencia en funcin del nivel del rbol en el que se encuentra y dentro de ese nivel de
la posicin ocupada de izquierda a derecha. Por ejemplo:

Figura 13
De forma que para un nodo representado por el elemento A[i] se cumple que:
(i) El nodo padre estar localizado en la posicin [(i-1) / 2], si i > 0.
El nodo raz, i=0, no tiene padre).

108 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
(ii) Si tiene hijos estarn localizados en las posiciones [2(i+1)-1] y [2(i+1)].
Si i > [(n+1) / 2 1], el nodo no tiene hijos.
Una caracterstica fundamental de esta estructura de datos es la propiedad que caracteriza a un
motculo puede ser restaurada eficazmente tras cualquier modificacin de un nodo (cambiar el valor
de la clave, insertar un nodo, borrar un nodo, etc.).

Representacin de montculos
Ya se ha comentado que por ser el montculo un rbol lleno, es eficiente su representacin mediante
arrays. De manera que la representacin del montculo contendr necesariamente un array donde
guardar la informacin. Esto supondr tener que estimar un mximo para la cantidad de elementos
que podemos guardar en el montculo.
Adems del array ser necesario llevar la cuenta de cuantos de los elementos del array son vlidos
(forman parte del heap).

const int MAX = ??

class Heap
{
public:
...
private:
typedef Valor Vector[MAX];
Vector info;
int num;
}

Operaciones bsicas sobre montculos


Supongamos que se trata de un montculo de mximos (cualquier comentario sobre este tipo de
montculos se puede extender fcilmente a los montculos de mnimos). En este caso, se debe
cumplir que la clave de cualquier nodo sea mayor o igual que las claves de sus hijos. Si se modifica
el valor de un nodo incrementando su valor, entonces es posible que la propiedad del montculo no
se cumpla con respecto a sus nodos antecesores, no respecto a sus hijos. Esto implicara ir
ascendiendo por el rbol comprobando si el nodo modificado es mayor que el padre, si es as
intercambiarlos y repetir el proceso en un nivel superior hasta que se encuentre que no es necesario
realizar ningn intercambio o que se llegue al nodo raz. En cualquier caso, tras ese proceso se
habr restaurado la propiedad del montculo. Este algoritmo quedara como sigue:

Algoritmo Subir
{* Se utiliza cuando se incrementa el valor del nodo situado en la posicion pos *}
Entradas
Heap arb
Posicion pos (ndice)

Inicio
Si ( (pos > 0) y (arb.Info[pos] > arb.Info[(pos 1) / 2] ) entonces
Intercambiar (arb.Info[pos] , arb.info[(pos 1) / 2] )
Subir (arb, (pos 1) / 2 )
Fin_si
Fin

Si por el contrario, modificamos un nodo haciendo disminuir su valor, el problema consistir en


comprobar la propiedad respecto a sus descendientes. Si la clave del nodo es mayor que las claves

Tema 14. rboles 109


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
de sus hijos, entonces la propiedad se sigue cumpliendo, si no es as, hay que intercambiar la clave
de este nodo con la del hijo que tenga la clave mxima y repetir todo el proceso desde el principio
con este nodo hijo hasta que no se realice el intercambio.

Algoritmo Bajar
{* Se utiliza cuando disminuye el valor del nodo situado en la posicion pos *}
Entradas
Heap arb
Posicion pos (ndice)

Variables
Posicin max (ndice)

Inicio
max pos
Si ( ( (2*(pos+1) 1) < n) y (arb.Info[2*(pos+1)-1] > arb.Info[max] ) ) entonces
max 2*(pos+1)-1
Fin_si

Si ( (2*(pos+1) < n ) y ( arb.Info[2*(pos+1)] > arb.Info[max] ) entonces


max 2*(pos+1)
Fin_si

Si ( max pos ) entonces


Intercambiar ( arb[pos], arb[max] )
Bajar (arb, max)
Fin_si
Fin

A partir de estos dos algoritmos bsicos que permiten restaurar la estructura del montculo, se
pueden definir las operaciones de insercin y borrado de elementos en esta estructura de datos.

Insercin
Al intentar aadir un nuevo elemento al montculo, no se sabe cul ser la posicin que ocupar el
nuevo elemento de la estructura, pero lo que si se sabe es que, por tratarse de un rbol completo,
dnde se debe enlazar el nuevo nodo, siempre ser en la ltima posicin de la estructura.
Por ejemplo, en el siguiente montculo formado por cinco elementos, si se intenta aadir un nuevo
elemento, sea el que sea, se conoce cul ser la estructura final del rbol:

Figura 14
Si se almacena en el nuevo nodo la informacin que se desea insertar, lo ms probable ser que la
propiedad del montculo no se conserve. Como el nuevo nodo siempre es terminal, para mantener la
propiedad del montculo bastar con aplicar el algoritmo subir a partir del nodo insertado, con lo
que el algoritmo de insercin resulta verdaderamente simple.

110 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I

bool Monticulo::Insertar (Monticulo::Valor x)


{
bool exito;

if (n == MAX)
exito = false;
else
{
exito = true;
info[n] = x;
n++;
Subir (n - 1);
}
return exito;
}

Borrar (una posicin pos cualquiera)


Al igual que en la operacin de insercin, hay que tener en cuenta que el montculo es un rbol
completo y que, para que se mantenga esa propiedad, el nodo que debe desaparecer realmente es el
que est situado en la ltima posicin. Por lo tanto, la estrategia a seguir para borrar cualquier nodo
del montculo podra ser: sustituir la informacin del nodo a borrar por la del tlimo nodo del
montculo y considerar que el rbol tiene un nodo menos (n-1); como con esta modificacin
seguramente se habr alterado la propiedad del montculo, entonces aplicar el algoritmo subir o
bajar, lo que sea preciso, para restaurar esa propiedad. La aplicacin del algoritmo subir o del
algoritmo bajar depender de que la modificacin de la informacin del nodo haya implicado un
incremento (aplicar subir) o una disminucin (aplicar bajar) de la clave.

Algoritmo Borrar

Entrada
Heap arb
Posicin pos (ndice)

Variable
x: Valor

Inicio
x arb.info[pos]
arb.Info[pos] arb.Info[arb.Num - 1]
arb.Num arb.Num - 1
Si(1) (arb.Info[pos] x) entonces
Si(2) (arb.Info[pos] > x ) entonces
Bajar (arb, pos)
Sino(2)
Subir (arb, pos)
Fin_si(2)
Fin_si(1)
Fin

Aplicacin de los montculos


Los montculos tienen como aplicacin fundamental el mantenimiento de las llamadas colas de
prioridad. Esta estructura de datos es un tipo especial de cola, donde cada elemento lleva asociado
Tema 14. rboles 111
Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert
un valor de prioridad, de forma que cuando se borra un elemento de la estructura, ste ser el
primero de los elementos con la mayor (menor) prioridad. En cualquier instante se puede insertar un
elemento con prioridad arbitraria. Si la utilizacin de la cola de prioridad requiere borrar el
elemento con la mayor (menor) prioridad, entonces se utiliza para su representacin un montculo
de mximos (mnimos), donde resulta inmediata la localizacin de ese elemento.
En un montculo de mximos resulta inmediato obtener el valor mximo de las claves del rbol.
Como se ha visto anteriormente, este valor est siempre situado en la raz del rbol, por lo tanto
bastara con acceder al primer elemento del array A[0].
En C++:

bool Monticulo::Maximo (Monticulo::Prioridad& res)


{
bool exito;

if (MonticuloVacio () )
exito = false;
else
{
exito = true;
res = arbol[0];
}
return exito;
}


Y borrar el mximo sera pues eliminar la posicin cero del montculo.

bool Monticulo::EliminarMax ()
{
bool exito;

if (MonticuloVacio () )
exito = false;
else
{
exito = true;

info[0] = info[n];
n--;
Bajar(0);
}
return exito;
}


Con estas operaciones definidas como bsicas quedara la interfaz de la clase como sigue:

class Monticulo //Monticulos binarios de maximos


{
public:
typedef int Valor;

Monticulo (void);

bool MonticuloVacio (void);

112 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
bool Maximo (Valor &);
bool EliminarMax (void);
bool Insertar (Valor);

private:
typedef Valor Vector[MAX];

Vector info;
int n;

void Subir (int);


void Bajar (int);
};


Quedara por implementar el constructor por defecto y el mtodo MonticuloVacio.
Veamos algunos ejemplos prcticos de colas de prioridad:

Ejemplo 1:
Supongamos que se desea informatizar la lista de espera para la realizacin de operaciones
quirrgicas en un quirfano de un hospital. Se desea utilizar el quirfano de la manera ms
eficiente posible, de forma que en todo instante se opere al enfermo que ms lo necesite
siguiendo el orden de la lista de espera. En ese caso, es posible asignar algn tipo de
prioridad a cada uno de los enfermos, en funcin, por ejemplo, de la gravedad de la
operacin, del tiempo de ocupacin del quirfano o incluso, del precio de la operacin. De
esta forma, cada vez que el quirfano quede libre se recurir a la lista de espera y se
seleccionar a aquel enfermo con la prioridad ms alta.
Desde el punto de vista de programacin del proceso, se podra utilizar una representacin
de la lista de espera en funcin de un montculo de mximos, siendo la informacin asociada
con los nodos del rbol los datos de cada enfermo y la clave de identificacin la prioridad de
los mismos. Cuando un enfermo ingresa para ser operado en el hospital, se le asigna una
prioridad y se insertan sus datos como un nuevo nodo del montculo (operacin insertar),
cuando el quirfano queda libre, se ocupar con el enfermo cuyos datos estn situados en la
raz del montculo (mxima prioridad) y se reestructurar el mismo.

Ejemplo 2:
La gestin de los recursos de un ordenador en un entorno multiusuario (CPU, memoria,
disco, perifricos, etc.) se suele realizar tambin utilizando colas de prioridad. El problema
se asemeja mucho al del ejemplo anterior. Existe un dispositivo que se desea utilizar y
existen varios posibles usuarios, por lo tanto hay que seleccionar aquel usuario que permita
utilizar de la manera ms eficiente posible dicho dispositivo. Para ello, el sistema operativo
asigna una prioridad a cada peticin de utilizacin, de manera que se atiende a las peticiones
en funcin de la prioridad y el orden temporal en que se han realizado. La prioridad asignada
a cada prioridad puede depender de varias cosas. el tipo de usuario que la realiza (en un
sistema multiusuario, no todos los usuarios tienen el mismo tratamiento, existen usuarios
con ms prioridad que otros), el tiempo de utilizacin del dispositivo solicitado
(normalmente un trabajo que requiera poco tiempo de utilizacin del dispositivo se deber
atender antes que otro que requiera mucho ms tiempo, de esta manera se agiliza el tiempo
de respuesta del sistema), el tiempo que hace que el usuario no accede a ese recurso, etc...
En base a todas estas consideraciones se puede establecer una prioridad a cada peticin de
utilizacin de un recurso del ordenador y mantener una cola de prioridad (generalmente un
montculo de mximos) para cada uno de estos recursos que gestione eficientemente su uso.

Tema 14. rboles 113


Algoritmos y estructuras de datos I Ricardo Ferrs / Jess Albert

14.11. Ordenacin con rboles


En los dos ltimos tipos de rboles binarios comentados, se ha visto como se puede organizar
eficientemente la informacin en estas estructuras en base a una clave de informacin, de manera
que sea fcil extraer informacin relacionada con esa clave (buscar por clave, acceder a la clave
mxima, acceder a la clave mnima, etc.). Esto hace que parezca evidente la utilizacin de estas
estructuras de datos para resolver un problema tan habitual como pueda ser el problema de la
ordenacin de informacin.
Si nos centramos en el problema de la ordenacin interna sobre arrays, que fue el problema
estudiado en su momento, se puede pensar en los montculos como el tipo de rboles que podran
resolver el problema. Hay que tener en cuenta que, normalmente, la representacin de los
montculos se hace precisamente con arrays y que en estas estructuras es muy fcil acceder al
elemento con clave mxima (o mnima, segn sea la organizacin del montculo), con lo que se
podra establecer algn tipo de algoritmo que aprovechando esta propiedad ordene la informacin
del array.
Si consideramos que inicialmente el array puede estar arbitrariamente ordenado, esto implicar que,
en general, no se cumplir la propiedad de montculo de mximos, por lo que habra que empezar
por formar un montculo a partir de ese array. Una vez formado el montculo, es fcil saber qu
elemento debe ocupar la ltima posicin del array ordenado, ese elemento siempre ser el que
ocupa la raz del rbol (el primero del array). Si se intercambia el primer elemento con el ltimo, se
habr logrado ubicar correctamente el elemento mximo. Se puede ahora considerar que el array
tiene un elemento menos (el que est bien colocado) y reorganizar el montculo con un elemento
menos. Si se repite este proceso hasta que se tenga un montculo con un nico elemento, se puede
asegurar que el array estar finalmente ordenado. Este mtodo de ordenacin se conoce con el
nombre de Heapsort y el algoritmo sera el siguiente:

Algoritmo Heapsort

Entrada
Array[0..n - 1] vect

Variable
Indice i

Inicio
Hacer_Heap (vect)
Desde i n - 1 hasta 1 hacer
Intercambiar (vect[0], vect[i])
Bajar (vect[0..i-1], 0)
Fin_desde
Fin

Para terminar, hara falta especificar cmo se puede construir un montculo de mximos a partir de
un array arbitrariamente ordenado. La solucin es bastante simple, ir formando montculos de
tamao creciente hasta formar uno que contenga los n elementos del array. Comenzando desde atrs
hacia adelante, se puede considerar que los n div 2 ltimos elementos son nodos terminales y que,
por lo tanto, forman montculos de tamao 1 que no requieren ningn tipo de reorganizacin. Si
consideramos ahora secuencialmente los elementos a partir de n div 2 hasta 1, es como si se fuese
aadiendo cada vez un nodo ms a cada uno de los montculos ya formados, como esta insercin
modifica la estructura del montculo corespondiente, es necesario reorganizarlo mediante el

114 Ricardo Ferrs / Jess Albert


Ricardo Ferrs / Jess Albert Algoritmos y estructuras de datos I
procedimiento bajar. Al final del proceso, se puede asegurar que el array estar organizado en forma
de montculo de mximos. El algoritmo sera el siguiente:

Algoritmo Hacer_Heap

Entrada
Array[0..n-1] vec

Variable
i: Indice

Inicio
Desde i (n 1) / 2 hasta 0 hacer
Bajar (vect, i)
Fin

Hay que tener en cuenta que tanto en este caso (algoritmo HacerHeap), como en el algoritmo
anterior (Heapsort) se est pasando al algoritmo Bajar un vector y no un heap, de manera que
habra que modificar convenientemente el algoritmo Bajar para que funcionase de forma correcta.
En cuanto al coste del algoritmo de ordenacin hay que tener en cuenta el coste de cada una de las
dos partes del algoritmo, Hacer_Heap y la reorganizacin de 'n-1' montculos mediante el algoritmo
Bajar. La formacin del montculo tiene un coste lineal con el tamao del array, O(n), mientras que
el coste del bucle que reorganiza los montculos de tamao decreciente es O(n*log n).
Asintticamente, la funcin logartmica domina a la lineal y el coste final del algoritmo de
ordenacin ser O(n*log n). Por lo tanto, resulta que el mtodo de ordenacin heapsort es ms
eficiente que los mtodos directos vistos en el tema dedicado a vectores.

Tema 14. rboles 115

You might also like