Documentos de Académico
Documentos de Profesional
Documentos de Cultura
de búsqueda
Juan Manuel Dodero Beardo
P06/75001/00581
Módulo 7
© FUOC • P06/75001/00581 • Módulo 7 Árboles de búsqueda
Índice
Introducción ............................................................................................ 5
Objetivos ................................................................................................... 6
Resumen .................................................................................................... 48
Solucionario ............................................................................................. 51
Glosario ..................................................................................................... 52
Bibliografía .............................................................................................. 52
Anexo ......................................................................................................... 53
© FUOC • P06/75001/00581 • Módulo 7 5 Árboles de búsqueda
Introducción
Objetivos
No obstante, cuando se trata de listar todos los elementos de una tabla siguien-
do un orden determinado, el comportamiento de las tablas de dispersión no es
todo lo bueno que sería deseable. Por ello se plantean estructuras de datos alter-
nativas que, manteniendo una complejidad aceptable para las búsquedas indi-
viduales, mejoren la eficiencia de las operaciones de listado sobre una parte
considerable de los elementos de la colección. En este módulo se estudiarán los
árboles de búsqueda como una solución alternativa a éstos y otros problemas.
Los árboles de búsqueda son una variante de los árboles ya estudiados. Los árboles El aprovechamiento de los árboles
como estructura de datos jerárquica se
estudia en el módulo “Árboles”
son estructuras de datos que reflejan relaciones jerárquicas entre sus elementos. de esta asignatura.
Así como las listas son lineales, se suele hablar de los árboles como estructuras
multidimensionales, dado que el número de relaciones jerárquicas entre cada ele-
mento y sus descendientes en el árbol se pueden ver como las distintas dimensio-
nes de una tabla. De hecho, cualquier árbol puede expresarse en forma de tabla
multidimensional, aunque en forma de tabla sea más difícil de dibujar.
• Todos los elementos situados por debajo y a la izquierda de otro deben te-
ner una clave de ordenación menor que éste.
• Todos los elementos situados por debajo y a la derecha de otro deben tener
una clave de ordenación mayor que éste.
Las relaciones jerárquicas entre los elementos del árbol se aprovechan para re-
presentar el orden de los elementos, tal y como ilustra el ejemplo de la figura
2, que refleja un árbol binario de búsqueda que utiliza números enteros como
clave de ordenación. En dicho árbol, todos los nodos situados por debajo y a
la izquierda del nodo 6 son números menores que él (nodos 2, 4 y 5); todos
los nodos situados por debajo y a la derecha del nodo 6 son números mayores
que él (nodos 7, 9 y 11); esta misma condición puede comprobarse para los
nodos 4 y 11. El árbol está ordenado y, por lo tanto, es un árbol de búsqueda.
Así, es fácil saber cuáles son menores y cuáles mayores a partir de un elemento
cualquiera, y se puede restringir considerablemente el espacio de búsqueda so-
© FUOC • P06/75001/00581 • Módulo 7 9 Árboles de búsqueda
Los árboles de búsqueda sirven para guardar estructuras lineales de elementos or- Complejidad asintótica
denados mejorando la complejidad de las búsquedas. Las búsquedas en un árbol
La mejora en la eficiencia que
se realizarán comparando la clave buscada con las claves de los elementos alma- se produce en los árboles de
búsqueda no siempre se tra-
cenados en cada nodo, empezando desde la raíz y descendiendo por el árbol. De- duce en una mejora de la com-
pendiendo de si la clave buscada es mayor o menor que la encontrada en cada plejidad asintótica. Para
asegurar esto, el árbol debe te-
nodo, se elige la rama del árbol por la que se desciende en la búsqueda. ner una cierta forma equilibra-
da que se estudiará más
adelante en este módulo.
Los elementos de un ABB están ordenados de acuerdo con una clave de or-
denación, que puede ser cualquier atributo del elemento. La clave de orde- Podéis ver la clave de ordenación en
el módulo “El TAD Tabla” de esta
asignatura.
nación puede estar constituida por uno o más atributos, como ya sabemos.
En un árbol binario de búsqueda, en principio, no puede haber elementos de
clave repetida, a menos que se amplíe la estructura del árbol con estructuras
adicionales que den soporte a esta circunstancia.
Por otro lado, el árbol de la figura 3b sí es un ABB, ya que todos los nodos tie- Árboles binario y binario
de búsqueda
nen por debajo y a su izquierda elementos menores que ellos, y por debajo y
Como los elementos que for-
a su derecha elementos mayores. Para comprobarlo, simplemente hay que ge- man el árbol a no están orde-
nados, este árbol no es de
nerar el recorrido en inorden del árbol: búsqueda.
Como los elementos que for-
• Recorrido en inorden del árbol 3a: {2, 7, 5, 6, 11, 5, 4, 9} man el árbol b están ordena-
dos, este árbol es de búsqueda.
• Recorrido en inorden del árbol 3b: {2, 4, 5, 6, 7, 9, 11}
Como resultado de recorrer un ABB
en inorden se obtiene una
colección ordenada de los
elementos del árbol.
1.2. Árboles multicamino de búsqueda
Los árboles binarios de búsqueda sirven para almacenar colecciones ordena- Otra posibilidad de los árboles
binarios de búsqueda es proporcionar
una implementación adicional para los
das de elementos y mejorar la complejidad de la operación de búsqueda en diccionarios estudiados en el módulo “El
TAD Tabla” de esta asignatura.
una lista ordenada. Por lo tanto, para estudiar la utilidad de los árboles bina-
rios de búsqueda, éstos se han de usar como implementación de algún TAD
que represente una colección ordenada de elementos (por ejemplo, una lista
ordenada por una cierta clave).
queda) para encontrar cualquier elemento en el árbol del ejemplo. Dado que un
árbol binario de esa altura puede contener como máximo n = 7 nodos, la com-
plejidad será aproximadamente log n.
Sin embargo, este coste no se alcanza en todas las situaciones, sino que depende
de la “forma” del árbol. Por ejemplo, el árbol de la figura 6 contiene los mismos
elementos que el anterior, pero realizar una búsqueda puede necesitar hasta 5
comparaciones (es decir, el tamaño máximo del camino de búsqueda). En este
caso, la complejidad es más cercana a O(n) que a O(log n)
b) Continuar la búsqueda.
La búsqueda progresa por uno de los dos lados de la posición de partida, pero no Búsqueda en un ABB
por ambos. En caso de que el elemento buscado sea menor que la raíz de dicha
De manera similar a como se
posición, sabemos que no estará en el subárbol derecho, así que podemos descar- hacía en la búsqueda binaria o
dicotómica sobre un vector or-
tar todos sus elementos y sólo hemos de buscar en el izquierdo; sucede lo mismo denado, en la búsqueda en un
con el subárbol izquierdo si el elemento buscado es mayor que la raíz. ABB también somos capaces
de descartar una parte de los
elementos para delimitar el es-
El algoritmo de la operación de búsqueda, abstraído de la operación consultar pacio de búsqueda.
La diferencia radica en el
disponible en la implementación del TAD ArbolBinarioBusquedaEncadenado, es hecho de que en la búsqueda
el siguiente: binaria se usan índices enteros
que delimitan el espacio de
búsqueda en el vector y, en
uoc.ei.tads.ArbolBinarioBusquedaEncadenado cambio, en un ABB se emplean
los hijos izquierdo y derecho
para descartar los subárboles
en los que es seguro que no se
Elem consultar(Posicion padre, Elem buscado) { halla el elemento.
boolean encontrado = false;
int comp;
while ( padre!=null && !encontrado ) {
comp = comparar(buscado, padre.getElem());
encontrado = comp==0;
if (comp <0) padre = hijoIzquierdo(padre);
else if (comp >0) padre = hijoDerecho(padre);
}
return encontrado ? padre.getElem() : null;
}
La inserción debe garantizar que el árbol resultante siga siendo un ABB, es de-
cir, que sus nodos continúen ordenados. Para ello, la inserción comienza bus-
cando la posición en la que debe insertarse el nuevo elemento. Si la posición
© FUOC • P06/75001/00581 • Módulo 7 15 Árboles de búsqueda
• Si el nuevo elemento es igual al del nodo visitado, se sustituye el contenido Repetición de elementos
del nodo por el nuevo elemento y la inserción termina.
Tal como ya hemos comenta-
do, los ABB no están diseñados
• Si el nuevo elemento es menor que el del nodo visitado, se desciende por para almacenar elementos re-
petidos. Por ello, en caso de
su izquierda. igualdad del elemento por in-
sertar con alguno del árbol, se
opta por sustituir este último.
• En cambio, si el nuevo elemento es mayor, se desciende por su derecha. Sin embargo, es posible cam-
biar este comportamiento y
La búsqueda se detiene si se llega al primer caso, o bien se alcanza un nodo hacer que el nuevo elemento
se acumule en una lista de coli-
vacío, que será justo la posición en la que hay que insertar el nuevo elemento. siones asociada al nodo, y así
poder almacenar las repeticio-
Una vez encontrada la posición, simplemente se inserta el elemento como un nes.
nodo nuevo y el algoritmo finaliza.
De momento, no nos hemos preocupado del equilibrio de los ABB. Sin embar-
go, observad cuándo hay que equilibrar el árbol tras cada inserción: en la llama-
da a la operación equilibrar(padre). La operación de inserción puede provocar la
aparición de un nuevo nodo en el árbol. Si se producen muchas inserciones, y
todas ellas van a parar a la misma zona del árbol, es posible que crezcan más
unas ramas que otras y el árbol acabe desequilibrándose. Recordad que es desea-
ble mantener el árbol equilibrado para que las búsquedas se mantengan con una
complejidad más cercana a logarítmica que a lineal. Más adelante estudiaremos
las operaciones de equilibrado, que serán necesarias después de insertar o borrar
elementos de un ABB.
Ejemplo
a) La supresión no siempre se acaba con una simple eliminación del nodo con-
tenedor del elemento encontrado, pues puede suceder que dicho nodo ocupe
una posición intermedia en el árbol, y su simple eliminación también elimina-
ría todos los nodos que cuelgan de él.
1) Fase de búsqueda
b) Si el elemento que se borra es menor que el del nodo visitado, se baja por
su izquierda.
2) Fase de eliminación
a) Que el nodo no tenga ningún hijo (es decir, es una hoja): entonces simple-
mente se elimina la referencia al nodo que guarda el padre.
b) Que el nodo sólo tenga un hijo (es totalmente indiferente que sea el iz-
quierdo o el derecho, pero no ambos a la vez): entonces se actualiza la referen-
cia en el padre para que guarde la del “nieto”, y se salte el hijo que se quiere
eliminar
c) Que el nodo tenga dos hijos no vacíos. Éste es el caso más complicado, pues
no se puede elegir simplemente uno de los dos hijos como sustituto, como en
el caso anterior (puede ser que el sustituto tenga también dos hijos y no ten-
dríamos sitio para colocarlos). En tal caso, hay que buscar el sustituto ideal. Si
se piensa en que el ABB guarda una colección ordenada de elementos, hay dos
sustitutos posibles: el elemento inmediatamente anterior o el posterior en di-
cho orden. En consecuencia, en este caso la supresión debe avanzar hacia la
subfase que consiste en localizar el mencionado sustituto con el que reempla-
zar el nodo borrado.
Cualquiera de las dos posibilidades es válida, pero sólo se puede elegir una de
ellas. Por ejemplo, si se opta por la primera opción, hay que buscar el elemento
mayor del subárbol izquierdo. Esta búsqueda consiste simplemente en bajar
por la rama derecha de todos los descendientes, hasta que se encuentre un
nodo sin hijo derecho. Una vez encontrado, dicho nodo será el sustituto, y
sólo queda que reemplace el nodo que se quiere borrar.
© FUOC • P06/75001/00581 • Módulo 7 19 Árboles de búsqueda
E elemBorrado=null;
if (padre == null) return null;
int comp = comparar(elem, padre.getElem());
if (comp <0)
elemBorrado = borrar(padre, hijoIzquierdo(padre), elem);
else if (comp >0)
elemBorrado = borrar(padre, hijoDerecho(padre), elem);
else {
elemBorrado = padre.getElem();
if (esHoja(padre))
super.borrar(donde, padre);
else if ((hijoIzquierdo(padre)!=null) &&
(hijoDerecho(padre)==null) )
sustituir(donde, padre, hijoIzquierdo(padre));
else if ((hijoIzquierdo(padre)==null) &&
(hijoDerecho(padre)!=null) )
sustituir(donde, padre, hijoDerecho(padre));
else {
Posicion<E> masPequeno = hijoDerecho(padre);
while (hijoIzquierdo(masPequeno)!=null)
masPequeno=hijoIzquierdo(masPequeno);
intercambiar(padre, masPequeno);
borrar(padre, hijoDerecho(padre), masPequeno.getElem());
}
}
equilibrar(donde);
return elemBorrado;
}
Como con la operación de inserción, la supresión puede provocar un desequi- Podéis ver el equilibrado de árboles
en el subapartado 2.2 de este
librio en el árbol que habrá que arreglar. Para ello se ha situado oportunamen- módulo didáctico.
Ejemplo
Este módulo ha comenzado afirmando que los ABB sirven para guardar colec- Recordad que para la medida de la
complejidad asintótica se tiene en
cuenta el peor de los casos (podéis ver el
ciones ordenadas de elementos, pero mejorando la eficiencia de las búsque- módulo “Complejidad algorítmica” de esta
asignatura).
das. Esto no siempre se traduce en una mejora de la complejidad asintótica de
las operaciones de búsqueda. A continuación se verá el porqué de este hecho.
Estudio de Wirth
Dicho de otro modo, la búsqueda en un ABB aleatorio exige como media un 39% más de
comparaciones que en uno perfectamente equilibrado. La mejora puede ser incluso ma-
yor en el caso más desfavorable en el que un ABB sin equilibrar degenera en una lista.
Se dice que un ABB está perfectamente equilibrado si, para cada nodo,
el número de nodos de sus subárboles izquierdo y derecho difiere como
máximo en una unidad.
El equilibrio perfecto es una condición bastante estricta que hay que exigir
a un ABB para conseguir una complejidad en las búsquedas de O(log n).
Cuando el número de actualizaciones es muy grande en comparación con las
búsquedas, habrá que ejecutar el algoritmo de equilibrado perfecto muchas
veces. En estas situaciones, la complejidad de O(log n) que tiene cada inserción
o supresión empeora, pues hay que sumarle la complejidad de la operación de
equilibrado, que es O(n).
Sin embargo, hay condiciones más laxas de equilibrio que pueden exigírsele a
un ABB para conseguir la mejora pretendida de eficiencia en las búsquedas, sin
por ello empeorar en demasía las operaciones de actualización. Uno de estos
casos es el denominado equilibrio en altura.
Un árbol está equilibrado en altura cuando, para cada uno de sus no-
dos, las alturas de sus subárboles izquierdo y derecho difieren como má-
ximo en una unidad.
Ejemplo
El árbol de la figura 11 está equilibrado en altura, ya que la diferencia de alturas entre las
ramas izquierda y derecha no supera la unidad en ningún subárbol.
Los árboles AVL son un tipo particular de árboles binarios de búsqueda equilibra-
dos en altura. La definición de equilibrio en altura se debe a G. M. Adelson-Velskii
y E. M. Landis, en cuyo honor estos árboles se conocen como árboles AVL.
Observación
Un árbol AVL es un árbol binario de búsqueda en el que las alturas de los
Fijaos en que la diferencia má-
subárboles de cualquier nodo difieren como máximo en una unidad. xima entre los subárboles iz-
quierdo y derecho en un AVL
se da en su altura, no en el nú-
mero de nodos, que puede ser
La ventaja principal que se obtiene de los árboles AVL es que las operaciones mayor.
Los árboles de Fibonacci son un tipo de árbol AVL, cuyo nombre proviene de Árboles de Fibonacci
la manera en que se construyen, similar a los conocidos números de Fibonacci.
Los árboles de Fibonacci son
los AVL más desequilibrados
La definición de árbol de Fibonacci es la siguiente: a que existen porque, exceptu-
ando las hojas, todos los
demás nodos presentan un
1) El árbol vacío es un árbol de Fibonacci de altura 0 y se denota por A0. desequilibrio exacto de una
unidad (bien a la izquierda o
bien a la derecha).
2) Un único nodo es un árbol de Fibonacci de altura 1 y se denota por A1.
nacci de altura h.
n ≤ 2h+1 − 1
log n ≤ h +1
El factor de equilibrio de un nodo es la diferencia entre las alturas de Medida del factor
sus subárboles derecho e izquierdo: de equilibrio
El factor de equilibrio de un nodo hay que actualizarlo con cada inserción o su-
presión que se haga en alguno de sus subárboles. Esto se realizará en puntos con-
cretos de los algoritmos de inserción y supresión, como se verá más adelante.
La inserción en un árbol AVL tiene dos fases, que se tratarán seguidamente por
separado: la actualización del factor de equilibrio de los nodos afectados y las ro-
taciones (si procede) para resolver desequilibrios provocados en la fase anterior.
Cuando haya que realizar alguna rotación, el nodo en el que hayamos detec-
tado el desequilibrio lo denominaremos nodo pivote. El nodo pivote es el an-
tecedente más cercano del nodo insertado que tenga Fe ≠ 0.
Las líneas discontinuas horizontales representan los niveles de profundidad del árbol; los círculos son nodos individuales, y los cuadrados son subárboles (pueden representar más de
un nodo); el nodo sombreado representa el lugar de la inserción del nuevo nodo.
© FUOC • P06/75001/00581 • Módulo 7 30 Árboles de búsqueda
Los nodos afectados por una rotación simple son el pivote (P) y la raíz del ma-
yor subárbol hijo (H) del pivote. En una rotación doble se ven afectados estos
mismos nodos y, además, la raíz del mayor subárbol nieto (N) del nodo H.
Tanto en rotaciones simples como dobles también se ve afectado el padre del
pivote, que no se ha representado en la figura en aras de una mayor claridad.
Leyenda de la figura 17
La supresión en un árbol AVL comienza por la búsqueda del nodo que se quie-
re borrar. Al igual que en los ABB, si se encuentra en un nodo hoja, se elimina.
Si el nodo es intermedio, hay que sustituirlo por el menor de su subárbol iz-
quierdo (o el mayor del derecho).
Una vez eliminado el nodo, hay que recalcular el factor de equilibrio del padre
del nodo borrado o movido. Si no se provocó un desequilibrio, sólo hay que
ajustar algunos factores de equilibrio. En cambio, si se ha producido un desequi-
librio, también habrá que reequilibrar el árbol mediante las rotaciones introdu-
cidas para el caso de la inserción.
se encuentra el nodo 45, con su Fe no nulo. Una vez ajustado éste y puesto a
Fe = 0, es necesario seguir el análisis y actualización de Fe, porque el acorta-
miento que se ha producido en la rama más larga del nodo 45 puede influir
en el Fe de nodos situados más arriba en el árbol, como sucede con el 40 y el
36, cuyos Fe deben ser actualizados: Fe(40) = 0 y Fe(36) = –1.
a) Si la raíz del subárbol más grande tiene Fe = 0, se realiza una rotación simple
(que dependerá del Fe del nodo analizado: si Fe > 0, la rotación será D-D; y si Fe < 0,
la rotación será I-I). Después se ajusta el factor de equilibrio del nodo analizado y
se detiene el análisis. En el árbol de la figura 20 se ha borrado el nodo 38. Como
el factor de equilibrio del nodo analizado es Fe(40) = +1, se ha borrado por la rama
más corta, y como la raíz de la rama más larga tiene Fe(48) = 0, hay que aplicar el
caso 3a. La rotación aplicada es la D-D porque el Fe del nodo analizado es positivo.
b) Si el nodo analizado y la raíz del subárbol más grande tienen el mismo Fe, se
realiza una rotación simple, se ajusta el factor de equilibrio de este último, pero el
análisis prosigue hacia los antecesores del nodo analizado (si los Fe son positivos,
la rotación será D-D, y si son negativos, la rotación será I-I). Por ejemplo, sobre el
árbol de la figura 21 se borrarará el nodo 38. Entonces se analiza el nodo 40, que
tiene un Fe no nulo. Además, se ha borrado por la rama más corta, y la rama más
larga es Fe(45) = Fe(40) = +1. Por lo tanto, hay que aplicar el caso 3b y realizar una
© FUOC • P06/75001/00581 • Módulo 7 33 Árboles de búsqueda
rotación D-D simple. Una diferencia notable con respecto al caso 3a es que ahora
el análisis no se detiene, sino que la variación del factor de equilibrio se sigue pro-
pagando hacia arriba en el árbol. Éste es el motivo por el que el Fe del nodo 36 fi-
nalmente valdrá –1, tras la aplicación del caso 1 en la propagación.
c) Si el nodo analizado y la raíz del subárbol más grande tienen Fe de distinto va-
lor, se realiza una rotación doble (I-D o D-I, dependiendo de los factores de equi-
librio de ambos), se ajustan sus factores de equilibrio y se analizan los antecesores.
En el árbol de la izquierda de la figura 22 se borra el nodo de clave 6. El análisis
comienza por el nodo 12. Tanto en el nodo 12 como en el 3 se reproduce el caso
2. Por ello, tras actualizar sus Fe, hay que seguir analizando hacia arriba. El siguien-
te que se analiza es el nodo 13 (con un Fe, +1). Si consideramos que el Fe del nodo
40 es –1, nos encontramos en el caso 3c. Por lo tanto, hay que realizar una rota-
ción doble D-I, cuyo resultado es el árbol de la derecha de la figura.
Observad que, en todos los casos, el criterio para decidir si es necesario seguir
analizando los nodos del recorrido hasta la raíz es que se modifique la altura del
subárbol que parte del nodo analizado. Si la altura no se modifica, no hay que
seguir analizando los antecesores restantes.
Los AVL no proponen un TAD nuevo con respecto a los árboles binarios de
búsqueda, ya que no amplían el conjunto de operaciones de manejo que estos
© FUOC • P06/75001/00581 • Módulo 7 34 Árboles de búsqueda
Los árboles AVL de la biblioteca de TAD están representados por la clase ArbolAVL, Patrón de diseño
que hereda la misma interfaz de la clase ArbolBinarioBusquedaEncadenado, y rede-
Es el modo como la superclase
fine sólo la implementación de ciertas operaciones de la superclase: en particular, delega la operación de equili-
brio hacia sus subclases es un
la inserción y la supresión. Afortunadamente, la clase ArbolBinarioBusquedaEnca- patrón de diseño muy común,
denado se ha diseñado teniendo en cuenta este factor, por lo que se ha concen- conocido como método planti-
lla (en inglés, template method)
trado en el método protegido equilibrar() toda la implementación de operaciones (Gamma y otros, 2002).
A continuación se verá cómo se usa un árbol binario de búsqueda AVL para alber- El TAD Diccionario se estudió junto
con las tablas de dispersión en el
módulo “El TAD Tabla” de esta
gar la colección de elementos del TAD Diccionario. Este TAD puede implementarse asignatura.
con distintos tipos de contenedores. Entre ellos, cabe destacar los árboles binarios
de búsqueda y las tablas de dispersión. A continuación se describe una implemen-
tación del TAD Diccionario basada en un árbol AVL, que posteriormente se com-
parará con la implementación basada en tablas de dispersión.
• constructor() O(1)
– Crea una instancia del árbol AVL que implementa el diccionario.
uoc.ei.tads.DiccionarioAVLImpl
public DiccionarioAVLImpl() {
avl = new ArbolAVL<ClaveValor<C,E>>();
}
• constructor() O(1)
– Crea una instancia del árbol AVL que implementa el conjunto.
uoc.ei.tads.ConjuntoAVLImpl Adaptador
Figura 23. Ejemplo de un paginador de registros procedentes de una base de datos de películas
No se muestra toda la lista de una vez, sino sólo un trozo de ella. El listado se acompaña de controles para avanzar y retroceder
entre trozos.
Fijaos en que, en este ejemplo, no suelen hacerse búsquedas esporádicas por cla-
ve en el diccionario que contiene todas las películas. Más bien, suelen hacerse
recorridos en bloques de k elementos, tantos como quepan en la pantalla. En tal
caso, una implementación basada en árboles AVL será más conveniente que
aquéllas basadas en tablas de dispersión.
© FUOC • P06/75001/00581 • Módulo 7 39 Árboles de búsqueda
uoc.ei.ejemplos.modulo7.Paginador
...
public interface Paginador<C,E> {
uoc.ei.ejemplos.modulo7.PaginadorImpl
...
public class PaginadorImpl<C,E> implements Paginador<C,E> {
private DiccionarioExtendido<C,E> diccionarioPaginas;
public PaginadorImpl() {
diccionarioPaginas = new DiccionarioExtendido<C,E>();
}
public Iterador<E> paginar(C claveInicial,C claveFinal) {
return diccionarioPaginas.elementos(claveInicial,claveFinal);
}
}
© FUOC • P06/75001/00581 • Módulo 7 40 Árboles de búsqueda
uoc.ei.ejemplos.modulo7.DiccionarioExtendido
...
public class DiccionarioExtendido<C,E> extends DiccionarioAVLImpl<C,E> {
public Iterador<E> elementos(C claveInicial,C claveFinal) {
Recorrido<ClaveValor<C,E>> rango= new
RecorridoRango<C,E>(avl.recorridoInorden(),claveInicial,claveFinal);
return new IteradorRecorridoValoresImpl<C,E>(rango);
}
}
uoc.ei.ejemplos.modulo7.RecorridoRango
...
public class RecorridoRango<C,E> implements Recorrido<ClaveValor<C,E>> {
private Recorrido<ClaveValor<C,E>> recorridoGlobal;
private C claveInicial,claveFinal;
private Posicion<ClaveValor<C,E>> siguiente;
Las bases de datos y los sistemas de ficheros suelen emplear índices para acce-
der más rápidamente a los elementos almacenados. Los índices suelen cons-
truirse empleando como clave un subconjunto de atributos de la colección de
elementos (por ejemplo, los nombres de los ficheros, o las claves de las tablas
de una base de datos). Los árboles de búsqueda multicamino están pensados
para construir estos índices de manera que se aprovechen las ventajas de los
árboles de búsqueda a la vez que se minimiza el número de accesos al disco.
Ejemplo
Supongamos que se quiere almacenar una colección ordenada de 220 elementos en un ár-
bol binario de búsqueda situado en un disco organizado en bloques con cabida para 100
nodos. Aun cuando el árbol esté equilibrado, dado su tamaño, en el peor de los casos se
necesitarían 20 accesos a disco para recuperar un elemento (el peor de los casos se da
cuando el elemento se sitúa en el nivel más profundo de la jerarquía y ningún par de
nodos de los recorridos para llegar a él comparten un mismo bloque del disco). Esto se
debe a que con un ABB, el número de accesos a disco es igual que el número de elementos
consultados, es decir, 20 en el peor de los casos.
Si conseguimos agrupar los nodos del ABB en bloques o páginas de nodos, como mues-
tra la figura 24, el número de accesos a disco será de log100 210 ∼ 1,5. Esto significa que,
con un máximo de dos accesos a disco, podemos alcanzar cualquier nodo.
1) Cada nodo (excepto la raíz) tiene como mínimo n/2 elementos. La raíz de un árbol B debe po-
der guardar un número de ele-
mentos por debajo del mínimo
2) Cada nodo tiene como máximo n elementos. permitido, ya que inicialmente
el árbol está vacío, o bien no
hay nodos suficientes, ni para
3) Si un nodo tiene m elementos, o bien es una hoja, o bien tiene m + 1 hijos. hacerlo crecer, ni para rellenar
la raíz.
5) Los nodos del árbol están ordenados como un árbol de búsqueda normal
(es decir, los que guardan elementos menores a la izquierda y los mayores a la
Orden de un árbol B
derecha).
Algunos autores definen el or-
den de un árbol B como el nú-
6) Todas las hojas se encuentran en el mismo nivel en el árbol. mero máximo de elementos de
un nodo. Otros lo definen
como la mitad de la capacidad
La figura 25 presenta un árbol B de orden 4 en el que se observan las caracte- máxima de elementos de un
nodo. Nosotros utilizamos la
rísticas de la definición.
primera definición.
En la figura 26, ∀i = 1, ..., m se cumple que ki < ki+1, y pi es una referencia a un nodo
cuyas claves son mayores que ki y menores que ki+1. Dicha secuencia se correspon-
de con un TAD ListaOrdenada, aunque la implementación más común es con una
matriz (array) estática, pues el orden del árbol no suele cambiar.
La clave que sube hacia el padre puede dar lugar a una nueva división, y esta si-
tuación se puede repetir hasta llegar a la raíz. Por ello se suele decir que los árboles
B crecen por la raíz, al contrario que los ABB, que lo hacen por las hojas.
a) Sustituir la clave por el mayor elemento del nodo apuntado por la referen-
cia al anterior (podéis ver la figura 28 para un ejemplo de esta alternativa).
b) Sustituir la clave por el menor elemento del nodo apuntado por la referen-
cia al siguiente.
Figura 29. Esquema del equilibrado de nodos al borrar una clave en un árbol B
© FUOC • P06/75001/00581 • Módulo 7 46 Árboles de búsqueda
Los árboles binarios de búsqueda usados por la JCF son una variedad conocida Web recomendada
como árboles rojinegros. Esta implementación proporciona una complejidad de
Puede encontrarse una
O(log n) para las operaciones de búsqueda (containsKey y get), inserción (put) y prueba de la eficiencia de
estos árboles en Wikipedia:
supresión (remove).
<http://en.wikipedia.org/
wiki/Red-black_tree>
© FUOC • P06/75001/00581 • Módulo 7 48 Árboles de búsqueda
Resumen
En este módulo se han estudiado los árboles binarios de búsqueda y varias im-
plementaciones que aseguran que las búsquedas en colecciones enlazadas de
elementos ordenados se pueden hacer de manera compleja O(log n). Para ello
se ha visto el concepto de equilibrio y dos de sus variantes: el equilibrio perfec-
to y el equilibrio en altura. Como ejemplo de árboles equilibrados en altura se
han estudiado los árboles AVL y sus operaciones de inserción y supresión.
También se han estudiado los árboles B, útiles para almacenar grandes colec-
ciones ordenadas de elementos en un disco. De estos árboles se ha visto su es-
tructura y cómo se realizan las operaciones de inserción y supresión.
© FUOC • P06/75001/00581 • Módulo 7 49 Árboles de búsqueda
Ejercicios de autoevaluación
Números de comparaciones en el
Número de comparaciones en el peor
n peor de los casos (árbol no
de los casos (árbol equilibrado)
equilibrado)
15
31
3. A partir del siguiente árbol AVL, borrad el nodo clave 10 e indicad las operaciones de ro-
tación necesarias y los factores de equilibrio antes y después de cada rotación.
4. Si definimos el grado de desequilibrio de un árbol AVL como el número de nodos cuyo factor
de equilibrio es diferente de cero, reflexionad y razonad sobre el porqué de la afirmación si-
guiente: “Los árboles de Fibonacci son árboles AVL con el grado máximo de desequilibrio po-
sible”.
5. Insertad en un árbol AVL inicialmente vacío las siguientes claves y en este mismo orden:
3, 8, 9, 2, 1, 5, 4, 6, 10 y 7. Indicad las rotaciones y los cambios de factor de equilibrio en
cada operación. Posteriormente, extraed los mismos valores en el orden inverso en el que
se van insertando.
© FUOC • P06/75001/00581 • Módulo 7 50 Árboles de búsqueda
6. Insertad en un árbol B de orden 4, inicialmente vacío, las claves siguientes y en este mismo
orden: 20, 40, 10, 30, 15, 35, 7, 26, 18, 22, 5, 42, 13, 46, 27, 8, 32, 38, 24, 45 y 25. Sobre
el árbol resultante, borrad la secuencia de claves siguiente: 25, 45, 24, 38, 32, 8, 27, 46, 13,
42, 5, 22, 18, 26, 7, 35 y 15. Indicad a cada paso el estado inicial y final del árbol.
7. Los árboles 2-3 son un tipo de árbol de búsqueda no binario en el cual cada nodo tiene
dos (nodo-2) o tres hijos (nodo-3). Un nodo-2 contiene un elemento y dos enlaces hijos
que, como en los árboles binarios de búsqueda, apuntan hacia elementos más pequeños
(los del hijo izquierdo) y más grandes (los del hijo derecho). Los nodos-2 pueden no tener
hijos, pero no pueden tener sólo uno. Un nodo-3 guarda exactamente dos elementos y tres
hijos (o bien ninguno). Si el nodo-3 tiene hijos, el hijo izquierdo contiene elementos más
pequeños que el primer elemento del padre; el hijo central contiene elementos más gran-
des que el primer elemento del padre, pero más pequeños que el segundo elemento; y el
hijo derecho contiene elementos más grandes que el segundo elemento del padre. La pro-
piedad más interesante de los árboles 2-3 es que, por construcción, todos los nodos hoja
están en un mismo nivel, por lo que no es necesario hacer operaciones de equilibrado adi-
cionales, siempre que se respete la definición de la estructura citada. Programad una im-
plementación alternativa DiccionarioArbol23Impl para el TAD Diccionario.
8. En el paginador diseñado en el apartado 2.6, podríamos haber optado por definir opera-
ciones para navegar por la colección de datos, como avanzar(k), que avanza y devuelve los
siguientes k elementos del diccionario, y análogamente retroceder(k). Implementad estas
operaciones y ampliad en consecuencia la interfaz de la clase DiccionarioExtendido.
9. La solución implementada para el paginador del subapartado 2.6 recorre todos los elemen-
tos del árbol AVL, para posteriormente filtrar aquellos que están comprendidos entre las
claves inicial y final pedidas por el método paginar. Una solución más idónea evitaría tener
que recorrer todos los elementos, filtrando directamente los solicitados en un solo recorri-
do, a medida que se construye el iterador devuelto por el método recorridoInorden() de la
clase ArbolAVL. Modificad la solución propuesta para que optimice la implementación del
método paginar de la manera descrita. (Nota: la clase ArbolAVL se puede ampliar con algún
método auxiliar para el recorrido que tenga en cuenta el rango de claves que se puede re-
correr.)
© FUOC • P06/75001/00581 • Módulo 7 51 Árboles de búsqueda
Solucionario
1.
Número de comparaciones en el peor Número de comparaciones en el peor
n
de los casos (árbol no equilibrado) de los casos (árbol equilibrado)
3 3 2
7 7 3
15 15 4
31 31 5
2. El análisis comienza por el nodo 10, antiguo padre del nodo 5 borrado. En el nodo 10 se da el
caso 2 de la supresión en un AVL, por lo que se anula su Fe y hay que continuar analizando.
El siguiente es el nodo 84 (que tenía un Fe de +1), que se ha borrado por la rama más corta. Por
lo tanto, nos encontramos en el caso 3. Para saber qué rotación hay que efectuar, cabe consi-
derar el Fe del nodo 86, que tiene el mismo valor que el del 10 (los dos son +1). Por lo tanto,
estamos en el caso 3b y hay que hacer una rotación simple (D-D en este caso, porque los dos
Fe son iguales a +1). El resultado es el árbol que vemos al margen:
3. Son necesarias dos rotaciones consecutivas: primero, una rotación D-D para equilibrar el
subárbol con raíz en la clave 50; y, después, una rotación D-I sobre el nodo 62, para equi-
librar el árbol resultante de la primera rotación.
© FUOC • P06/75001/00581 • Módulo 7 52 Árboles de búsqueda
4. Los árboles de Fibonacci, por la manera de construirse, son árboles AVL que no tienen nin-
gún nodo con factores de equilibrio diferente de cero, por lo que son árboles AVL con el gra-
do de desequilibrio más grande posible.
Glosario
árbol AVL m Tipo de árbol binario de búsqueda en el cual las alturas de los subárboles de
cualquier nodo difieren como máximo en una unidad.
árbol B m Tipo de árbol multicamino de búsqueda que limita el número de claves de cada
nodo y asegura que el árbol se mantenga siempre equilibrado en altura.
árbol binario de búsqueda m Árbol binario empleado para almacenar colecciones orde-
nadas de elementos enlazados de manera que se agilicen las operaciones de búsqueda.
sigla ABB
equilibrio en altura m Equilibrio que mantiene un ABB cuando, para cada nodo, las altu-
ras de sus subárboles izquierdo y derecho difieren como máximo en una unidad.
equilibrio perfecto m Equilibrio que mantiene un ABB cuando, para cada nodo, el núme-
ro de nodos de sus subárboles izquierdo y derecho difieren como máximo en una unidad.
factor de equilibrio m Diferencia entre las alturas de los subárboles derecho e izquierdo
de un nodo.
Bibliografía
Bibliografía básica
Aho, A. V.; Hopcroft, J. E.; Ullman, J. D. (1988). Estructuras de datos y algoritmos. Buenos
Aires: Addison Wesley Iberoamericana.
Bayer, R.; McCreight, E. (1972). “Organization and maintenance of large ordered in-
dexes”. Acta informatica (núm. 1, pág. 173-189).
Gamma, E.; Helm, R.; Johnson, R.; Vlissides, J. (2002). Patrones de diseño. Addison-
Wesley.
Goodrich, M. T.; Tamassia, R. (2003). Data structures and algorithms in Java (3.a ed.). John
Wiley & Sons.
Preiss, B. R. (1999). Data structures and algorithms with object-oriented design patterns in Java.
John Wiley & Sons.
Weiss, M. A. (1995). Estructuras de datos y algoritmos. Upper Saddle River: Addison Wesley.
Anexo