ESTRUCTURAS DE DATOS Y ALGORITMOS Escuela Técnica Superior de Informática Aplicada Curso 2004-2005 Práctica 8. Implementación de operaciones de la clase Árbol Binario de Búsqueda Duración: 2 sesiones 1 Objetivo de la práctica El objetivo de esta práctica es realizar tareas de implementación de estructuras de datos. En concreto, y partiendo de la clase ArbolBinariodeBusqueda, se tendrá que implementar el método que resuelva la búsqueda del sucesor de un elemento dado del árbol. 2 Descripción del problema 2.1 Sucesor de un dato en un árbol binario de búsqueda Supóngase un árbol binario de búsqueda como el de la figura 1.a, en el que sus objetos son Integer. Considérese el nodo x (que no tiene hijo izquierdo), y la rama del árbol que conduce desde el nodo raíz hasta x, de cuyos nodos se dirán que son los ascendientes de x. Para cualquier nodo n de esta rama, se puede observar que: • si x aparece en su subárbol izquierdo, n.dato>x.dato, y los datos que aparezcan en su subárbol derecho serán aún mayores que n.dato; se dirá que n es un ascendiente por la derecha, • análogamente, si x aparece en su subárbol derecho, n.dato<x.dato, y los datos que aparezcan en su subárbol derecho serán aún menores que n.dato; se dirá que n es un ascendiente por la izquierda. En consecuencia, el menor de los datos del árbol mayores que x.dato, aparece en alguno de los nodos ascendientes por la derecha de x. Considérese y, el más cercano de ellos a x. Por el mismo razonamiento, sus nodos ascendientes por la derecha en la rama considerada, contendrán datos mayores que y.dato. En resumen, y.dato es el siguiente valor a x.dato de entre todos los contenidos en el árbol. a) b) 100 100 >100 50 50 y <50 y 75 75 >75 52 x 52 x <52 68 68 <68 71 Figura 1 69 73 Adicionalmente, y redundando en lo anterior, se observa que el nodo y es el siguiente visitado al nodo x en un recorrido inorden del árbol. En la variante de la figura 1.b, el nodo x sí tiene subárbol izquierdo no vacío, cuyos nodos seguirán a x en un recorrido inorden: el mínimo dato encontrado en x.der será el sucesor de x.dato. Considérese el ejemplo de la figura 2; el sucesor de 85 y de 100 están en el ascendiente por la derecha más cercano: el nodo que contiene a 100 y null respectivamente (el sucesor de 100 no existe). El sucesor de 25 y el de 50 están en el mínimo del respectivo subárbol derecho: nodos con datos 30 y 60, respectivamente. 100 50 25 10 75 30 60 85 Figura 2 Si en el ejemplo de la figura anterior se considera únicamente la parte del árbol que arranca del nodo n.izq (figura 3), el sucesor de 85 resulta ahora null. n 100 n.izq 50 25 10 75 30 60 85 Figura 3 De todo lo anterior, se sigue un método de obtención del sucesor de una cierta clave d, que admite una sencilla expresión iterativa: Supuestas unas variables x, ascenDer, de tipo NodoBinario y que respectivamente referencien al nodo a inspeccionar y a su ascendiente por la derecha más cercano, se irán actualizando de la forma adecuada estas variables mientras queden nodos por revisar y no se haya encontrado que x.dato coincida con d. La búsqueda empezará a partir del nodo n que se indique. El caso en que la búsqueda termine sin encontrar d, se tratará mediante una excepción. El nodo que se devuelva deberá ser ascenDer o el mínimo de x.der según el caso. La actividad 3.1 de esta práctica está dedicada a la implementación de esta operación dentro de la clase ArbolBinarioDeBusqueda. 2 2.2 Incorporación de la operación sucesor al modelo Diccionario Una de las implementaciones clásicas del modelo Diccionario se basa en los árboles binarios de búsqueda. En particular, en la práctica 6, se consideró la siguiente organización de clases: • paquete modelos: contiene, entre otras, la interfaz Diccionario, • paquete jerarquicos: contiene las clases NodoBinario, ArbolBinarioDeBusqueda, y ABBDiccionario, entre otras. Esta última implementa la interfaz Diccionario, y para ello extiende a la clase ArbolBinarioDeBusqueda, de la que se puede decir que concentra el mayor esfuerzo en el desarrollo de las operaciones. Considérese la posibilidad de hacer un modelo de Diccionario más extenso que el contemplado hasta el momento, y que, por ejemplo, incluyese una función para averiguar el elemento sucesor a una dado. Si la interfaz Diccionario incluye este método, deberá ser implementado por toda aquella clase que implemente a la interfaz, como es el caso de ABBDiccionario. En la actividad 3.2 de esta práctica se llevará a cabo esta posibilidad, partiendo del hecho de que en la clase ArbolBinarioDeBusqueda se ha incluido el método sucesor discutido en el apartado anterior. 3 Actividades en el laboratorio 3.1 Implementación y prueba de la operación sucesor Se partirá del fichero ArbolBinarioBusqueda.java, que se añadirá al paquete jerarquicos, obteniéndolo de /labos/asignaturas/EI/eda/practica7. Se editará esta clase para añadirle un método con el siguiente perfil: protected NodoBinario sucesor(Object clave, NodoBinario n) throws ElementoNoEncontrado y que, partiendo del subárbol que arranca del nodo n, calculará el nodo cuyo dato coincida con el sucesor de clave (null en el caso en que clave sea el máximo elemento del subárbol). Si clave no aparece en el subárbol, se producirá la excepción correspondiente, la cual precisará que no se ha encontrado el elemento al buscar el sucesor. La implementación de este método deberá ser iterativa, según las indicaciones del apartado 2.1. Para probar la corrección del método en el entorno bluej, se sugiere, en primer lugar, crear una instancia a de la clase ABBDiccionario. Se le insertarán sucesivamente los elementos Integer 100, 50, 25, 75, 10, 30, 60 y 85, para configurar la estructura de la figura 2. Dado que esta clase habrá heredado el método anterior, se procederá a probar su funcionamiento aplicándolo al objeto a. Se probarán los casos de búsqueda del sucesor sobre a, partiendo del nodo a.raiz, y a.raiz.izq, que se detallan en el apartado 2.1. 3.2 Ampliación del modelo Diccionario Una vez modificada y probada la clase ArbolBinarioDeBusqueda, se accederá al código fuente de la clase ABBDiccionario en el fichero ABBDiccionario.java, siguiendo la ruta /labos/asignaturas/EI/eda/practica7/, y depositándolo en el paquete jerarquicos. Se añadirá a la clase ABBDiccionario un método con el siguiente perfil: public Object sucesor(Object clave) throws ElementoNoEncontrado que devolverá el sucesor de clave en el árbol sobre el que se aplique el método. Para su implementación, se tendrá en cuenta que esta clase hereda el método sucesor de la clase ArbolBinarioDeBusqueda. Entonces, obtener el objeto sucesor de clave, consistirá en 3 extraer el dato del nodo devuelto por super.sucesor(clave,this.raiz), (null si el sucesor no existe). Se incluirá el perfil de este método en la interfaz Diccionario.java del paquete modelos, para que el cálculo del sucesor se pueda utilizar en las aplicaciones del modelo. Como una aplicación concreta, se modificará el TestTraductor de la práctica 6 para que entre sus opciones, aparezca la de buscar las tres entradas sucesoras a una clave dada. Se probará sobre el diccionario bilingüe que se habrá cargado desde el fichero datos2.txt, accesible también en la ruta que se menciona más arriba. 4