Instituto de Computación. Facultad de Ingeniería. Universidad de la República Examen de Programación 2 03 de Agosto de 2006 Generalidades: • • • • • • • La prueba es individual y sin material. La duración es 3 horas. Sólo se contestan dudas acerca de la letra de los ejercicios. Escriba las hojas de un sólo lado y con letra clara. Comience la solución de cada ejercicio en una nueva hoja. Numere cada hoja, indicando en la primera el total. Coloque su número de cédula y nombre en cada hoja. Ejercicio 1 (40 puntos: 25 parte (a) y 15 parte (b)) Considere la siguiente declaración, en Módula-2, del tipo de los árboles binarios de búsqueda de enteros que almacenan en cada nodo la cantidad de elementos (nodos) en su subárbol izquierdo más 1, además del dato: ABB = POINTER TO ABNode; ABNode = RECORD Dato : INTEGER; CantSubIzq : CARDINAL; Izq : ABB; Der : ABB; END; Ejemplo: Dato: 6, CantSubIzq: 5 6 Dato: 2, CantSubIzq: 3 2 -1 9 4 Dato: 9, CantSubIzq: 1 Dato: -1, CantSubIzq: 2 Dato: 4, CantSubIzq: 1 -7 Dato: -7, CantSubIzq: 1 Se pide, sin usar funciones o procedimientos auxiliares y accediendo directamente a la representación: a) Escribir en Módula-2 una función recursiva de inserción de un entero en un árbol binario de búsqueda de tipo ABB, de tal manera que cada nodo luego de la inserción guarde en el campo CantSubIzq la cantidad de elementos en su subárbol izquierdo más 1. La función debe retornar TRUE si el entero se inserta en el árbol (no estaba previamente) y FALSE en caso contrario. Asuma que en el árbol parámetro, el campo CantSubIzq guarda el valor apropiado para cada nodo (esto es, la cantidad de elementos en su subárbol izquierdo más 1). La función NO debe recorrer explícitamente más de un camino del árbol. En el árbol del ejemplo previo los caminos (expresados como listas) son 3: [6,2,-1,-7], [6,2,4] y [6,9]. PROCEDURE Insertar(VAR t : ABB; e : INTEGER) : BOOLEAN; b) Escribir en Módula-2 una función iterativa que dados un número natural k y un árbol binario de búsqueda de tipo ABB, retorne un puntero al k-ésimo menor elemento del árbol. Si no hay al menos k elementos en el árbol o k es cero, la función debe retornar el puntero NIL. Si k es 1, nos referimos al menor elemento del árbol, si k es 2 al segundo elemento más pequeño del árbol y así sucesivamente. Asuma que en el árbol parámetro, el campo CantSubIzq guarda el valor apropiado para cada nodo (esto es, la cantidad de elementos en su subárbol izquierdo más 1). La función no debe recorrer explícitamente más de un camino del árbol. PROCEDURE kMenor(t : ABB; k : CARDINAL) : ABB; Ejercicio 2 (60 puntos: 10 parte (a), 20 parte (b) y 30 parte (c)) Una coordenada es un tipo de datos de nombre Coord que contiene un par (X, Y) de elementos de tipo INTEGER. El TAD Coord contiene las siguientes operaciones: • • • • PROCEDURE CrearCoord (X, Y : INTEGER): Coord; (* Crea una coordenada con el par (X, Y) *) PROCEDURE CoordX (C : Coord): INTEGER; (* Retorna el primer elemento de C. *) PROCEDURE CoordY (C : Coord): INTEGER; (* Retorna el último elemento de C. *) PROCEDURE DestruirCoord (VAR C : Coord); (* Destruye la coordenada C *) a) Se debe especificar el TAD ConjCoord (Conjunto de coordenadas) con las operaciones usuales de conjuntos más las siguientes operaciones: • Dado un entero X, y un conjunto de coordenadas A se debe retornar un conjunto de coordenadas con todas las coordenadas de A que contienen a X como primer elemento del par. • Dado un entero Y, y un conjunto de coordenadas A se debe retornar un conjunto de coordenadas con todas las coordenadas de A que contienen a Y como último elemento del par. b) Suponga que el tipo de datos ConjCoord tiene la siguiente representación: TYPE ConjCoord = POINTER TO RECORD c : Coord; sig : ConjCoord END; Implemente la operación que retorna la diferencia entre dos conjuntos de coordenadas. Si usa funciones o procedimientos auxiliares, deberá implementarlos. c) Utilizando las operaciones del TAD ConjCoord implemente la operación: PROCEDURE CoordenadasInternas(C1, C2 : Coord; Conj : ConjCoord): ConjCoord; (* Retorna todas las coordenadas de Conj que se encuentran dentro del rectángulo formado por los puntos (C1.X, C1.Y), (C2.X, C1.Y), (C2.X, C2.Y), (C1.X, C2.Y). Si hay elementos en el borde del cuadrado también deben aparecer en el conjunto resultado. *) La implementación NO deberá iterar explícitamente sobre todas las coordenadas del rectángulo determinado por las coordenadas C1 y C2. Por ejemplo: Conj [(1,2), (3,5), (1,1)] [(1,-2), (3,-5), (-1,-1)] [(1,-2), (3,-5), (-1,-1)] C1 (0,0) (0,0) (1,0) C2 (3,5) (3,5) (3,-2) Resultado [(1,2), (3,5), (1,1)] [] [(1,-2)] SOLUCIONES Ejercicio 1.a) Considere la siguiente definición extendida de árbol binario de búsqueda de enteros: TYPE ABB = POINTER TO ABBNode; ABBNode = RECORD Dato : INTEGER; CantSubIzq : CARDINAL; Izq : ABB; Der : ABB; END; La cual almacena en cada nodo la cantidad de hijos izquierdos + 1, se pide la implementación de una función recursiva que inserte un elemento en dicho árbol manteniendo consistente la estructura del mismo, y retornando TRUE si el elemento se insertó en el árbol y FALSE en caso contrario: (* Función que inserta el elemento k en el ABB t, manteniendo consistente la información de la cantidad de hijos izquierdos. Retorna TRUE si k efectivamente se insertó en el árbol (no estaba previamente) y FALSE en caso contrario. *) PROCEDURE Insertar(VAR t : ABB; k : INTEGER): BOOLEAN; BEGIN IF (t = NIL) THEN NEW(t); t^.Dato := k; t^.CantSubIzq := 1; t^.Izq := NIL; t^.Der := NIL; RETURN TRUE ELSE IF (k < t^.Dato) THEN IF (Insertar(t^.Izq, k)) t^.CantSubIzq := t^.CantSubIzq + 1 ; RETURN TRUE ELSE RETURN FALSE END ELSIF (k > t^.Dato) THEN RETURN Insertar(t^.Der, k) ELSE RETURN FALSE END END END Insertar; Ejercicio 1.b) Usando la definición anterior de ABB, se pide la construcción de una función tal que dado un árbol del tipo ABB y un natural k, retorne el k-ésimo elemento menor del árbol: (* Función que retorna el k ésimo elemento menor de t. Asumimos que el árbol no tiene elementos repetidos. *) PROCEDURE KMenor(t : ABB; k : CARDINAL) : ABB; BEGIN IF (k = 0) THEN RETURN NIL ELSE WHILE (t <> NIL) AND (k <> t^.CantSubIzq) DO IF (k < t^.CantSubIzq) THEN t := t^.Izq ELSE k := k - t^.CantSubIzq; t := t^.Der END END; RETURN t END END KMenor; Ejercicio 2.a) (*************** Constructoras ***************) PROCEDURE Vacio () : ConjCoord; (* retorna el conjunto vacío *) PROCEDURE Insertar (C : Coord; A : ConjCoord) : ConjCoord; (* retorna un nuevo conjunto que consta de todos las coordenadas de A más la coordenada C, si es que C no era ya un elemento de A *) (**************** Predicados ****************) PROCEDURE EsVacio (A : ConjCoord) : BOOLEAN; (* retorna TRUE si A es vacío *) PROCEDURE Pertenece (C : Coord; A : ConjCoord) :BOOLEAN; (* retorna TRUE si C es una coordenada de A *) (**************** Auxiliares ****************) PROCEDURE Union (A, B : ConjCoord) : ConjCoord; (* retorna el conjunto de las coordenadas que pertenecen a A, o B o a ambos *) PROCEDURE Intersec (A, B : ConjCoord) : ConjCoord; (* retorna el conjunto de las coordenadas que pertenecen a A y a B *) PROCEDURE Dif (A, B : ConjCoord) : ConjCoord; (* retorna el conjunto de las coordenadas que pertenecen a A y no pertenecen a B *) PROCEDURE SubconjX (X : INTEGER; A : ConjCoord) :ConjCoord; (* retorna un conjunto de coordenadas con todas las coordenadas de A que contienen a X como primer elemento del par *) PROCEDURE SubconjY (Y : INTEGER; A : ConjCoord) :ConjCoord; (* retorna un conjunto de coordenadas con todas las coordenadas de A que contienen a Y como último elemento del par *) (**************** Destructoras ****************) PROCEDURE DestruirConjCoord (VAR A : ConjCoord); (* Destruye el conjunto de coordenadas A *) Ejercicio 2.b) PROCEDURE Vacio () : ConjCoord; BEGIN RETURN NIL; END Vacio; PROCEDURE EsVacio (A : ConjCoord) : BOOLEAN; BEGIN RETURN A = NIL; END EsVacio; PROCEDURE Pertenece (C : Coord; A : ConjCoord) :BOOLEAN; VAR res: BOOLEAN; BEGIN IF EsVacio(A) THEN res := FALSE; ELSE res := (CoordX(A^.c) = CoordX(C)) AND (CoordY(A^.c) = CoordY(C)) OR Pertenece(C, A^.sig); END; RETURN res; END Pertenece; PROCEDURE Insertar (C : Coord; A : ConjCoord) :ConjCoord; VAR res: ConjCoord; BEGIN IF NOT Pertenece(C, A) THEN NEW(res); res^.c := C; res^.sig := A ELSE res := A END; RETURN res; END Insertar; PROCEDURE Dif (A, B : ConjCoord) : ConjCoord; VAR res, iterador: ConjCoord; BEGIN res := Vacio(); iterador := A; WHILE NOT EsVacio(iterador) DO IF NOT Pertenece(iterador^.c, B) THEN res := Insertar(iterador^.c, res); END; Iterador := iterador^.sig; END; RETURN res; END Dif; Ejercicio 2.c) PROCEDURE CoordenadasInternas(C1, C2 : Coord; Conj : ConjCoord): ConjCoord; VAR i, incremento: INTEGER; res1, res2: ConjCoord; BEGIN (* Se calcula en res1 el conjunto de coordenadas con X dentro del rango *) incremento := (CoordX(C2) - CoordX(C1)) / ABS(CoordX(C2) - CoordX(C1)); i := CoordX(C1)-incremento; res1 := Vacio(); REPEAT i := i + incremento; res1 := Union(res1, SubconjX(i, Conj)); UNTIL i = CoordX(C2); (* Se calcula en res2 el subconjunto de res1 con Y dentro del rango *) incremento := (CoordY(C2) - CoordY(C1)) / ABS(CoordY(C2) - CoordY(C1)); i := CoordY(C1)-incremento; res2 := Vacio(); REPEAT i := i + incremento; res2 := Union(res2, SubconjY(i, res1)); UNTIL i = CoordY(C2); RETURN res2; END CoordenadasInternas;