Programación 2 Práctico 9 - TADs Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario Objetivos Trabajar con los tipos abstractos de datos Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario. Desarrollar y analizar implementaciones para estos TADs. Implementaciones Avanzadas. Primera parte: Especificaciones de Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario. Ejercicio 1 Desarrollar una especificación funcional (sólo funciones, no procedimientos) para el TAD Árbol Binario de Búsqueda (Binary Search Tree BST) de caracteres, la cual contenga un conjunto mínimo de constructores, selectores, predicados y destructores para: Crear un BST vacío. Determinar si un BST es vacío o no Inserción y supresión de un carácter en un BST. Saber si un carácter pertenece un BST. Ejercicio 2 Las funciones de la Figura 1 presentadas en el Práctico 4, forman una especificación para el TAD Árbol Finitario (Finitary Tree FT) de naturales (ver Figura 1). Los Árboles n-arios son un caso particular de los finitarios, donde cada nodo del árbol tiene n hijos. ¿Qué diferencias hay entre la especificación de Árboles Finitarios y n-arios? 1 2 FTree nullFTree (); /* Devuelve el arbol vacío */ 3 4 5 FTree consFTree ( unsigned int x , ListFTree l ); /* Crea un árbol no vacío a partir de un natural y una lista de hijos ( subárboles ) */ 6 7 8 bool IsEmptyFTree ( FTree t ); /* Determina si un árbol dado es o no vacío */ 9 10 11 unsigned int rootFTree ( FTree t ); /* Devuelve el valor en la raíz de un árbol no vacío */ 12 13 14 ListFTree offspringFTree ( FTree t ); /* Devuelve la lista de hijos ( subárboles ) de la raíz de un árbol no vacío */ Figura 1: Especificación del TAD Árbol Finitario (Finitary Tree FT) de naturales. Segunda parte: Implementaciones de Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario. Ejercicio 3 Desarrollar una implementación completa del TAD Árbol Binario de Búsqueda del Ejercicio 1 Ejercicio 4 Desarrollar una implementación completa del TAD Árbol Finitario de naturales del Ejercicio 2, utilizando la representación de primer hijo- siguiente hermano. Instituto de Computación - Facultad de Ingeniería - UdelaR Página 1 de 4 Programación 2 Práctico 9 - TADs Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario Ejercicio 5 Desarrollar una implementación completa del TAD Árbol n-ario de naturales del Ejercicio 2, que tome provecho de que todos los nodos del árbol tienen n hijos. ¿Qué representación considera adecuada para representar los hijos? Tercera parte: Implementaciones Avanzadas Ejercicio 6 (Segundo Parcial 2009) Suponga que se define una variante de árboles binarios de búsqueda (BST) balanceados según la cantidad de nodos de los subárboles. Se considera que un BST está balanceado si para cada nodo se cumple que la diferencia de la cantidad de nodos de sus subárboles (izquierdo y derecho) difiere en a lo sumo una unidad. Considere la declaración del tipo de los árboles binarios de búsqueda de enteros que se presenta en la Figura 2a, donde cada nodo de un árbol de tipo BST guarda en el campo CN la cantidad de nodos del árbol cuya raíz es dicho nodo. Recordar que la cantidad de nodos de un árbol vacío es 0, por definición. La Figura 2b presenta un ejemplo gráfico. Cada nodo se representa por un par de valores donde la primer componente es el valor de dato y el segundo el de CN para dicho nodo. ⊥ representa el árbol vacío. (10,8) 1 2 3 4 5 6 struct BSNode { int dato ; unsigned int CN ; BSNode * izq , * der ; }; typedef BSNode * BST ; (5,3) (3,1) (25,4) (8,1) (15,2) ⊥ (a) Definición de tipo para el TAD BST balanceado (30,1) (20,1) (b) Ejemplo Figura 2: BST balanceado. 6 Implementar una función recursiva insertar que, dados un árbol binario de búsqueda de enteros A de tipo BST, y un entero x, inserte a x en A y retorne TRUE, si y sólo si, luego de la inserción cada nodo cumple la condición de balanceo previamente definida. Tenga en cuenta las siguientes consideraciones: Si x ya estaba en A, la función no deberá modificar el árbol y el resultado será TRUE. La función insertar debe dejar el campo CN de cada nodo consistente con su definición, asumiendo que en el árbol parámetro se cumple que para cada nodo el campo CN contiene la cantidad de nodos del árbol cuya raíz es dicho nodo y además, que cada nodo (del árbol parámetro) cumple la condición de balanceo referida. La función insertar debe recorrer un sólo camino del árbol parámetro. No se permite usar funciones o procedimientos auxiliares en este ejercicio, salvo las funciones que se presentan en la Figura 3, las cuales se consideran predefinidas. Cabe aclarar que no se pide balancear el árbol, sino simplemente insertar según el criterio de los árboles binarios de búsqueda, actualizar los campos CN, que correspondan, y retornar un booleano que indique si luego de la inserción se cumple la condición de balanceo definida, asumiendo que ésta originalmente se verificaba en cada nodo del árbol parámetro. La firma de la función insertar es la siguiente: 1 bool insertar ( BST &A , int x ); Instituto de Computación - Facultad de Ingeniería - UdelaR Página 2 de 4 Programación 2 Práctico 9 - TADs Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario unsigned int abs ( int x ); /* Retorna el valor absoluto de x */ 1 2 3 4 5 caso */ unsigned int cantNodos ( BST A ); /* Retorna 0 si A es el árbol vacío y el valor del campo CN de la raíz de A en otro Figura 3: Funciones auxiliares para el Ejericio 6. Ejercicio 7 (Segundo Parcial 2011) Considere una agenda de teléfonos implementada como un árbol general de caracteres (de ’0’ a ’9’), donde la raíz no tiene información y cada nodo representa a un dígito de números telefónicos. Las listas de hermanos están ordenadas de forma lexicográfica. El camino de la raíz a una hoja determina un número telefónico de la agenda, guardándose en cada hoja el nombre del contacto asociado a ese número. Por ejemplo, el árbol de la figura Fig.4 contiene los contactos: 104 – bombero, 105 – ambulancia, 109 – patrullero, 16 – hora y 911 – emergencia. () 9 1 ⊥ 6 hora 0 4 bombero 5 ambulancia 1 ⊥ 1 emergencia 9 patrullero Figura 4: Ejemplo de agenda. Se pide: Definir la representación en C* del tipo Agenda, árbol general de caracteres implementado como un árbol con semántica “primer hijo-siguiente hermano”, con la información del contacto en las hojas. Considere que el tipo de la información del contacto es: 1 typedef char * InfoContacto ; Implementar, accediendo directamente a la representación, una función ObtenerContacto que, dados un número de teléfono y una agenda, retorne la información del contacto asociado a ese número. La función tiene como precondiciones que el número de teléfono NO es vacío y que debe tener exactamente un contacto asociado (esto es, que indica un camino de la raíz a una hoja). 1 InfoContacto ObtenerContacto ( Telefono tel , Agenda agenda ); El tipo Telefono, de los números de teléfono, es una lista de caracteres. Utilice las operaciones Primero y Resto del TAD Lista para operar sobre el parámetro tel; considere que estas operaciones ya están implementadas. La operación Resto retorna un alias al resto de la lista parámetro (sin su primer elemento). Instituto de Computación - Facultad de Ingeniería - UdelaR Página 3 de 4 Programación 2 Práctico 9 - TADs Árbol Binario de Búsqueda, Árbol Finitario y Árbol n-ario Implementar, accediendo directamente a la representación, una función ImprimirAgenda que imprima en pantalla la información guardada en una Agenda que se pasa como parámetro. 1 void ImprimirAgenda ( Agenda agenda ); El formato de impresión, que muestra la estructura de árbol finitario que representa la agenda, se especifica con el siguiente ejemplo. Si tenemos una variable miagenda con el árbol de ejemplo (ilustrado en la Fig.4), entonces ImprimirAgenda(miagenda) deberá imprimir: 1 -0 –4=bombero –5=ambulancia –9=patrullero -6=hora 9 -1 –1=emergencia Ejercicio 8 Para trabajar con árboles balanceados es útil guardar en cada nodo información para verificar condiciones de equilibrio. Por ejemplo, en los árboles llamados AVL (que son árboles ABB balanceados) puede guardarse en cada nodo la altura del árbol que tiene a dicho nodo como raíz. Se pide, utilizando la siguiente definición de los árboles binarios de búsqueda de enteros, con información de la altura en cada nodo: 1 2 3 4 5 6 struct ABBNodo { int dato ; unsigned int altura ; ABBNodo * izq , * der ; }; typedef ABBNodo * ABB ; Figura 5: Definición de tipo para el TAD ABB balanceado. 1. Implementar un procedimiento que dado un ABB de enteros que guarda en cada nodo, además del dato, el valor de su altura, y dado un entero, inserte a dicho elemento en el ABB (si ya no estaba) manteniendo la información de la altura de cada nodo. Notar que no se pide balancear el árbol, sino simplemente insertar el elemento en el ABB dejando en cada nodo en el campo altura el valor correspondiente, asumiendo que el árbol parámetro guarda en el campo altura de cada nodo el valor correcto. Este procedimiento no puede usar funciones ni procedimientos auxiliares. 2. Implemente una función booleana que retorne TRUE si y sólo si un ABB (del tipo definido anteriormente) es un AVL. Esto es, se cumple que para cada nodo del árbol la altura de sus subárboles izquierdo y derecho difiere a lo sumo en 1. El árbol vacío es un AVL. Esta función no puede usar funciones ni procedimientos auxiliares. 3. Indique los órdenes de tiempo de ejecución en el peor caso del procedimiento definido en la parte 1 y de la función definida en la parte 2. Justifique brevemente. Instituto de Computación - Facultad de Ingeniería - UdelaR Página 4 de 4