Instituto de Computación. Facultad de Ingeniería. Universidad de la República Solución Examen de Programación 2 Agosto 2005 Considere la siguiente declaración, en MÓDULA-2, del tipo ListaEnt de listas encadenadas de enteros: ListaEnt = POINTER TO NodoEnt; NodoEnt = RECORD info: INTEGER; sig: ListaEnt; END; Implemente en MÓDULA-2 una función F que dadas dos listas ordenadas (en forma ascendente) de enteros, C1 y C2, y sin elementos repetidos que representan a dos conjuntos, retorne una nueva lista ordenada y sin elementos repetidos que represente al conjunto (C1-C2)∪(C2-C1). A tener en cuenta: • La nueva lista no deberá compartir registros de memoria con las listas parámetros. • Se requiere que esta función recorra a lo sumo una vez cada lista parámetro. • No se permite usar TADs auxiliares. • Si usa funciones o procedimientos auxiliares deberá implementarlos, pero recuerde que cada una de las listas pueden recorrerse a lo sumo una vez (se usen o no funciones o procedimientos auxiliares). El cabezal de la función es el siguiente: PROCEDURE F (C1, C2 : ListaEnt) : ListaEnt; (* La siguiente función retorna una lista con los elementos exclusivos de cada una de las listas parámetro, sin compartir memoria con éstas. *) PROCEDURE F (c1, c2 : CardLista) : CardLista; VAR result, iter : CardLista; BEGIN NEW (result); (* celda dummy *) iter := result; WHILE ((c1 <> NIL) AND (c2 <> NIL)) DO IF (c1^.info <> c2^.info) THEN NEW (iter^.sig); iter := iter^.sig; IF (c1^.info < c2^.info) THEN iter^.info := c1^.info; c1 := c1^.sig ELSE iter^.info := c2^.info; c2 := c2^.sig END ELSE c1 := c1^.sig; c2 := c2^.sig END END; IF (c1 = NIL) THEN c1 := c2 (* la lista eventualmente no vacía queda en c1 *) END; WHILE (c1 <> NIL) DO NEW (iter^.sig); iter := iter^.sig; iter^.info := c1^.info; c1 := c1^.sig END; iter^.sig := NIL; iter := result; result := result^.sig; DISPOSE (iter); (* se borra la celda dummy *) RETURN (result) END F; 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 ABB balanceados) puede guardarse en cada nodo la altura del árbol que tiene a dicho nodo como raíz. En la siguiente figura se puede ver un ejemplo de la información que poseen los nodos de esta clase de árboles: 6 h=2 h=1 h=0 2 h=3 3 9 4 h=0 h=1 10 h=0 1 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: ABB = POINTER TO ABBNodo; ABBNodo = RECORD info: INTEGER; altura: CARDINAL; izq, der: ABB; END; a) 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. b) 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. a) PROCEDURE Insertar (VAR t : ABB; x : INTEGER); BEGIN IF (t = NIL) THEN NEW(t); t^.info := x; t^.altura := 0; t^.izq := NIL; t^.der := NIL ELSIF (x < t^.info) THEN Insertar(t^.izq, x); IF (t^.altura = t^.izq^.altura) THEN t^.altura := t^.altura + 1 END ELSIF (x > t^.info) THEN Insertar(t^.der, x); IF (t^.altura = t^.der^.altura) THEN t^.altura := t^.altura + 1 END END END Insertar; b) PROCEDURE EsAVL (t : ABB) : BOOLEAN; BEGIN IF (t = NIL) OR ((t^.izq = NIL) AND (t^.der = NIL)) THEN RETURN TRUE ELSIF (t^.izq = NIL) THEN RETURN (t^.der^.altura = 0) ELSIF (t^.der = NIL) THEN RETURN (t^.izq^.altura = 0) ELSE RETURN (ABS(t^.izq^.altura – t^.der^.altura) <= 1) AND EsAVL(t^.izq) AND EsAVL(t^.der) END END EsAVL; Se quiere especificar e implementar, en MÓDULA-2, un TAD ColaDoble que permita insertar y eliminar elementos (de a uno por vez) al comienzo y al final de una estructura lineal. Se pide: a) Especificar, incluyendo pre y postcondiciones, el TAD ColaDoble de elementos de un tipo genérico adecuado para modelar una estructura lineal no acotada, con operaciones que permitan: Crear la estructura vacía, Agregar un elemento al comienzo de la estructura lineal, Agregar un elemento al final de la estructura lineal, Verificar si la estructura es vacía, Eliminar y retornar el elemento al comienzo de la estructura lineal (si la estructura es no vacía), Eliminar y retornar el elemento al final de la estructura lineal (si la estructura es no vacía), DEFINITION MODULE ColaDbl; TYPE CDoble; (***** Constructoras *****) PROCEDURE Null (VAR C : CDoble); (* retorna la cola vacía *) PROCEDURE EnqueueFront (dato : T; VAR C : CDoble); (* inserta el dato al inicio de la cola C *) PROCEDURE EnqueueBack (dato : T; VAR C : CDoble); (* inserta el dato al final de la cola C *) (***** Predicado ******) PROCEDURE IsEmpty (C : CDoble) : BOOLEAN; (* Retorna TRUE si C es vacía *) (***** Selectoras *****) PROCEDURE Front (VAR dato : T; VAR C : CDoble); (* Pre : C no es vacía Retorna el elemento que se encuentra al comienzo de la cola C y lo quita de ella *) PROCEDURE Back (VAR dato : T; VAR C : CDoble); (* Pre : C no es vacía Retorna el elemento que se encuentra al final de la cola C y lo quita de ella *) END ColaDbl. Desarrollar una estructura para implementar el TAD anterior, sin usar TADs auxiliares, de tal manera que todas las operaciones se realicen sin recorrer la estructura elegida. Desarrollar el código de las siguientes 3 operaciones: la que crea la estructura vacía; la que elimina y retorna un elemento al final; y la que agrega un elemento al comienzo. Omita el código del resto de las operaciones del TAD. La estructura debe contemplar la restricción de que todas sus operaciones se realicen sin necesidad de recorrerla. Para ello puede ser implementada mediante una lista doblemente encadenada y con apuntadores al primero y último elemento. De esta forma se permite el acceso al principio y final de la cola sin necesidad de recorrerla. IMPLEMENTATION MODULE ColaDbl; FROM Storage IMPORT ALLOCATE, DEALLOCATE; TYPE (* definición de la celda básica de la cola *) Celda = POINTER TO NCelda; NCelda = RECORD info : CHAR; (* información *) sig : Celda; (* puntero al siguiente *) ant : Celda (* puntero al anterior *) END; (* definición de la celda cabezal de la cola *) CDoble = POINTER TO NCabezal; NCabezal = RECORD primero : Celda; (* puntero a la primera celda *) ultimo : Celda (* puntero a la última celda *) END; PROCEDURE Null (VAR C : CDoble) ; BEGIN (* se crea la celda cabezal *) NEW(C); (* se crea una celda dummy *) NEW(C^.primero); C^.primero^.sig := NIL; C^.primero^.ant := NIL; C^.ultimo := C^.primero END Null; PROCEDURE EnqueueFront(dato : T; VAR C : CDoble); (* agrega un elemento al principio *) VAR q : Celda; BEGIN (* Crea una celda para contener el nuevo elemento *) NEW(q); q^.info := dato; q^.sig := C^.primero^.sig; q^.ant := C^.primero; (* Coloca la nueva celda luego de la celda dummy *) C^.primero^.sig := q; (* Se verifica si es el primer elemento que se agrega a la estructura*) IF C^.ultimo = C^.primero THEN (* es el único elemento en la estructura entonces se actualiza el puntero al último *) C^.ultimo := q ELSE (* sino, la segunda celda debe apuntar a la nueva celda *) q^.sig^.ant := q END END EnqueueFront; PROCEDURE Back (VAR dato : T; VAR C : CDoble); (* Retorna el primer elemento de la cola y lo borra *) VAR delete : Celda; BEGIN dato := C^.ultimo^.info; (* se apunta a la última celda, la cual se quierre borrar *) delete := C^.ultimo; (* se actualiza el puntero al último *) C^.ultimo := C^.ultimo^.ant; C^.ultimo^.sig := NIL; (* se borra la celda *) DISPOSE(delete) END Back; END ColaDbl.