Instituto de Computación. Facultad de Ingeniería. Universidad de la República Solución Examen de Programación 2 Diciembre 2005 Ejercicio 2 – Parte a: Ejercicio 2 – Parte a: Ejercicio 1: Solución PROCEDURE F (L: ListaEnt; K: CARDINAL): ListaEnt; VAR result, iter, aux : ListaEnt; cant, cont : CARDINAL; BEGIN cant := 0; iter := L; WHILE(iter <> NIL) DO cant := cant + 1; iter := iter^.sig END; IF (cant > K) THEN FOR cont := 1 TO (cant-K) DO L := L^.sig END END; NEW (result); (* celda dummy *) iter := result; WHILE(L <> NIL) DO NEW (aux); aux^.info := L^.info; iter^.sig := aux; iter := aux; L := L^.sig END; iter^.sig := NIL; aux := result; result := aux^.sig; DISPOSE (aux); (* se borra la celda dummy *) RETURN result; END F; Ejercicio 2 – Parte a: Se plantea una estructura a partir de un campo donde se guarda la información y un arreglo para los sucesores. Solución TYPE ARBOL = POINTER TO NODO_ARBOL; NODO_ARBOL = RECORD dato : INTEGER; hijos : POINTER TO ARRAY [1..N] OF ARBOL END; Ejercicio 2 – Parte b: Se implementa con una función auxiliar recursiva para no tener que chequear condiciones de borde en forma repetida. La función principal verifica que el árbol no es vacío. La función auxiliar comprueba la condición de orden parcial tomando como precondición que el árbol no es vacío. Solución PROCEDURE ConOrdenParcialRecursivo (a : ARBOL) : BOOLEAN; VAR i : CARDINAL; hayOrden : BOOLEAN; BEGIN hayOrden := TRUE; IF (a^.hijos # NIL) THEN i := 1; WHILE (hayOrden AND (i <= N)) DO IF (a^.hijos^[i] # NIL) THEN hayOrden := (a^.dato > a^.hijos^[i]^.dato) AND ConOrdenParcialRecursivo (a^.hijos^[i]) END; INC (i) END END; RETURN hayOrden END ConOrdenParcialRecursivo; (* Solucion de la parte b *) PROCEDURE ConOrdenParcial (a : ARBOL) : BOOLEAN; VAR hayOrden : BOOLEAN; BEGIN hayOrden := TRUE; IF (a # NIL) THEN hayOrden := ConOrdenParcialRecursivo (a) END; RETURN hayOrden END ConOrdenParcial; Ejercicio 2 – Parte c: Se ofrecen dos soluciones: una recursiva y una iterativa. Ambas son válidas pero resulta más clara la primera. El procedimiento consiste en verificar si el nodo actual no tiene hijos. En ese caso se borra ese nodo y se termina el proceso. En otro caso, se busca el mayor de sus hijos, se coloca éste en lugar de su padre y se continúa el proceso a partir de ese hijo. Solución (* Solucion recursiva de la parte c *) PROCEDURE BorrarMaximoRec (VAR a : ARBOL); VAR i : CARDINAL; max, posMax : INTEGER; BEGIN IF (a # NIL) THEN IF (a^.hijos = NIL) THEN (* Si no tiene hijos se borra el nodo *) DISPOSE (a); a := NIL ELSE (* En otro caso se busca el mayor *) max := MIN (INTEGER); posMax := -1; FOR i := 1 TO N DO IF (a^.hijos^[i] # NIL) AND (max <= a^.hijos^[i]^.dato) THEN max := a^.hijos^[i]^.dato; posMax := i END END; IF (posMax # -1) THEN a^.dato := max; BorrarMaximoRec (a^.hijos^[posMax]); ELSE (* El vector de hijos esta vacio *) DISPOSE (a^.hijos); DISPOSE (a); a := NIL END END END END BorrarMaximoRec; (* Solucion iterativa de la parte c *) PROCEDURE BorrarMaximo (VAR a : ARBOL); VAR i, cantHijos : CARDINAL; max, posMax : INTEGER; iter : ARBOL; parar : BOOLEAN; BEGIN IF (a # NIL) THEN IF (a^.hijos = NIL) THEN (* Si no tiene hijos se borra el nodo *) DISPOSE (a); a := NIL ELSE (* En otro caso se busca el mayor *) parar := FALSE; iter := a; WHILE (NOT parar) DO cantHijos := 0; max := MIN (INTEGER); FOR i := 1 TO N DO IF (iter^.hijos^[i] # NIL) THEN IF (max <= iter^.hijos^[i]^.dato) THEN max := iter^.hijos^[i]^.dato; posMax := i END; INC (cantHijos) END END; iter^.dato := max; IF (iter^.hijos^[posMax]^.hijos = NIL) THEN (* El vector de hijos esta vacio, se corta la iteracion *) parar := TRUE; DISPOSE (iter^.hijos^[posMax]); iter^.hijos^[posMax] := NIL; IF (cantHijos = 1) THEN (* Era el ultimo hijo, se borra el vector *) DISPOSE (iter^.hijos); iter^.hijos := NIL END ELSE (* Se avanza iter *) iter := iter^.hijos^[posMax] END END END END END BorrarMaximo; Ejercicio 3: Ejercicio 3 – Parte a: Solución DEFINITION MODULE ColaPrio; TYPE CPrio; Rango = [1 .. M]; PROCEDURE empty (VAR C : CPrio); (* Retorna la cola vacía *) PROCEDURE insert (dato : T; ident : Rango; prio : INTEGER; VAR C : CPrio); (* Pre : NOT IsFull(C) Pre : No existe elemento con identificador ident en C Inserta el dato en la cola C con identificador ident y prioridad prio *) PROCEDURE IsEmpty (C : CPrio) : BOOLEAN; (* Retorna TRUE si C es vacía *) PROCEDURE IsFull (C : CPrio) : BOOLEAN; (* Retorna TRUE si C es llena *) PROCEDURE max (VAR dato : T; C : CPrio); (* Pre : NOT IsEmpty(C) Retorna en dato el elemento de mayor prioridad *) PROCEDURE deleteMax (VAR C : CPrio); (* Pre : NOT IsEmpty(C) Elimina el elemento de mayor prioridad en la cola C *) PROCEDURE decrPrio (ident : Rango; decr : CARDINAL; VAR C : CPrio); (* Decrementa el valor de prioridad del elemento ident en decr unidades si ident se encuentra en la cola C *) END ColaPrio. Ejercicio 3 – Parte b: Solución Las restricciones establecidas se pueden satisfacer utilizando un Heap y una Tabla que permita encontrar a cada elemento del Heap dada su clave. 1 31 2 19 24 3 4 13 21 16 5 6 7=M Dado que la cantidad máxima de elementos está acotada en M, y que el rango de los identificadores es [1..M], las estructuras pueden ser: • Heap: arreglo con tope (de 1 a M) que almacene elementos de tipo T junto a su prioridad e identificador. • Tabla: arreglo de 1 a M que almacene enteros de rango [0..M] que indican la posición en el Heap del elemento que tiene como identificador el índice del arreglo (donde 0 podría indicar la no existencia de dicho elemento). 1 2 3 4 5 6 7 2 1 3 6 4 5 (2,31) (1,24) (3,19) (6,13) (7,21) (4,16) 1 2 3 4 5 6 7 tope = 6 La operación max se soluciona obteniendo el primer elemento del Heap, lo cual no requiere recorrer la cola de prioridad. La operación isFull se soluciona verificando que el tope del arreglo sea igual a M, lo cual no requiere recorrer la cola de prioridad. Para resolver la operación decrPrio, primero se debe obtener la posición en el Heap del elemento al cual se va a decrementar su prioridad, esto se logra utilizando la Tabla (sin recorrer la cola). 1 2 3 4 5 6 7 2 1 3 6 4 5 (2,31) (1,14) (3,19) (6,13) (7,21) (4,16) 1 2 3 4 5 6 7 tope = 6 Luego se realizan los filtrados necesarios en el Heap, lo cual requiere log2(n) comparaciones en el peor caso, actualizándose a su vez la Tabla. Dado que los elementos se alamacenan en el Heap junto con sus identificadores, la actualización de la Tabla no requiere recorridas adicionales. 1 2 3 4 2 1 3 6 (2,31) (1,14) (3,19) (6,13) (7,21) (4,16) 1 2 3 4 5 6 tope = 6 5 6 7 4 5 7 De esta manera se cumple con la restricción de que la operación decrPrio realice log2(n) comparaciones en el peor caso, las operaciones insert y deleteMax se resuelven de forma similar. 1 2 3 4 5 1 3 6 (2,31) (7,21) (3,19) (6,13) (1,14) (4,16) 1 2 3 4 5 6 tope = 6 TYPE RangoExt = [0 .. M]; Nodo = RECORD ident : Rango; prio : INTEGER; dato : T; END; CPrio = RECORD map : ARRAY Rango OF RangoExt; heap : ARRAY Rango OF Nodo; tope : RangoExt; END; 5 6 7 4 2 7