Estructuras de Datos 1 GUÍA DE EJERCICIOS 1. Implementar el operador (función recursiva) Invertir(L,Q) que invierta el acceso y el sentido de los enlaces de una lista lineal de enlace simple L. Considerar la invocación Invertir(L,L). 2. Se dispone de una multilista M, en la cual todos los átomos son números enteros. Implementar el operador Reducir(M) que reduzca a un único átomo cada una de las secuencias que preceda o suceda a una sublista. El valor de ese nodo átomo será el resultante de sumar todos aquellos valores que originalmente conformaban la correspondiente secuencia. 3. Implementar operador Print(T) que imprime el contenido de un árbol de expresión T, incorporando los paréntesis necesarios. 4. Con respecto a una matriz dispersa C, de N3 valores enteros, la cual tiene Z elementos nulos: a) Determinar, analíticamente, la cantidad de posiciones de memoria que se ahorran en su representación compacta, primero con tres índices y luego con un índice. b) Evaluar para N=10 y Z=900 (en ambos casos). 5. Una implementación de polinomios, de dos variables reales y coeficientes enteros, con exponentes implícitos y grado G < 100, por ejemplo, es: Polinomio = stat array[0..99,0..99 of integer; donde "stat array" define un arreglo estático. Sin embargo, para polinomios dispersos resulta ser más eficiente en espacio una implementación con exponentes explícitos, como, por ejemplo: Polinomio = auto array[0..M of integer; donde "auto array" define un arreglo semidinámico. Según la implementación con exponentes explícitos de un polinomio P de dos variables: a) Establecer una expresión para el máximo valor de M en función de G, si la cantidad de coeficientes nulos representa, a lo menos, el 80%. b) Establecer una expresión para la cantidad A de bytes que se ahorran, respecto de la representación con exponentes implícitos, si P tiene N coeficientes nulos y cada valor entero ocupa 2 bytes. c) Obtener M y A para G = 99 y N = 8000. 6. Sea b la representación compacta de una matriz dispersa B de orden N2. El operador Valor(b,i,j) retorna el valor existente en la posición [i,j] de B. Implementar el operador Nonulos(b) que determina la cantidad de elementos no nulos que hay en la diagonal principal de B. Héctor Pincheira Conejeros Estructuras de Datos 2 7. Sea b la representación compacta de una matriz dispersa B de orden N2. El operador Valor(b,i,j) retorna el valor existente en la posición [i,j] de B y el operador Agrega(b,i,j,e) agrega el elemento e en la posición [i,j] de B. Haciendo uso de los citados operadores, implementar el operador Suma(s,b) que genera una matriz s resultante de la suma entre b y la traspuesta de b. 8. El TAD GreatInt se define como un entero de longitud "ilimitada". Luego, para dos variables i, j, de tipo GreatInt, las operaciones definidas y sus equivalentes convencionales son: Mas(i,j) i+j Menos(i,j) ij Por(i,j) i*j Div(i,j) ij Exp(i,j) ij Por otra parte, la representación dinámica de un polinomio de coeficientes y exponentes de tipo GreatInt, puede presentar la siguiente forma: type Polinomio = ^Termino; Termino = record coef, expo : GreatInt; link : Polinomio; end; Según estos antecedentes y con respecto a un punto x, también de tipo GreatInt, implementar: a) El operador ValueTerm(T,x) que evalúa un término de una variable de tipo Polinomio. b) El operador recursivo ValuePoly(P,x) que evalúa un polinomio P en el punto x. 9. Implementar el operador Ordenar(S1,S2) que genera un stack S2, ordenado, a partir de un stack S1, desordenado, utilizando auxiliarmente un stack S0. 10. Se sabe que un stack S siempre contiene, al menos, un elemento. Implementar, de manera recursiva, el operador Ultimo(S) que saca y retorna sólo el último elemento presente en S, sin afectar la disposición de los restantes elementos. 11. Un polinomio se representa mediante dos stacks P, Q que contienen los coeficientes (enteros), el primero, y los exponentes, el segundo. Los stacks están bien formados, es decir, tienen la misma cantidad de elementos y existe una correspondencia posicional entre coeficiente y exponente para cada término del polinomio. Implementar, recursivamente, el operador Unificar(P,Q) que consiga representar únicamente en el stack P el polinomio descrito, de manera que cada coeficiente quede sobre el respectivo exponente. 12. Con respecto a un stack S, de números enteros, implementar: a) El operador Bottom(S) que saca el elemento situado en el fondo de S, sin que éste pierda las caracaterísticas de definición y orden de sus restantes elementos. b) El operador recursivo Invertir(S) que invierte el orden de los elementos de S, utilizando sólo una variable auxiliar entera x y el operador Bottom ya implementado. Héctor Pincheira Conejeros Estructuras de Datos 3 13. Implementar el operador Invertir(S) que invierte el orden de los elementos contenidos en un stack S, utilizando auxiliarmente una cola C. 14. Para representar la forma binaria de un número entero no negativo se utiliza un stack de modo que el dígito del extremo derecho se sitúa en el top y el del extremo izquierdo en el bottom. Implementar el operador Decimal(S) que retorna el valor decimal del número representado en un stack S. El tipo base de S es el subrango 0..1. 15. Implementar los operadores Agrega y Extrae definidos sobre el TAD cola, utilizando sólo los operadores definidos sobre el TAD stack. 16. En una cola Q es posible advertir la sistemática repetición consecutiva de ciertos valores. Sin embargo, para efectos prácticos, tal repitencia no tiene validez alguna. Implementar el operador Reducir(Q) que reduce a una la ocurrencia de los elementos consecutivamente repetidos, sin alterar la distribución original de los elementos en la cola Q. 17. Una cola refleja es una cola cuyos elementos son los mismos de una cola directa, pero en orden inverso. Implementar, recursivamente, el operador Reflejar(C1,C2) que concluya con las versiones directa y refleja de una cola. Suponer que la cola inicialmente sin datos ya ha sido creada. 18. En un sistema computacional multiusuario, los procesos en espera por la asignación del procesador se encuentran en una cola Q. El sistema tiene la calidad de estable, es decir, que la cantidad de unidades de tiempo que cada proceso requiere para ejecutarse se conoce anticipadamente. Luego, el tipo base de la cola Q es una cantidad de unidades de tiempo. Iniciada la operación del sistema, se asigna el procesador al primer proceso durante una unidad de tiempo, es decir, se saca de la cola y, transcurrida la unidad, retorna a la cola (si no ha completado su ejecución) con su cantidad de unidades disminuida en uno. No considerando los tiempos de transición y suponiendo que existe una cantidad constante de procesos en espera, implementar el operador Terminados(Q,T) que determina el número de procesos que han abandonado la cola Q, por haber completado su ejecución, al concluir la T-ésima unidad de tiempo. 19. Se desea alterar la ubicación original de los elementos contenidos en una cola C, de modo que estos queden ordenados (de menor a mayor) desde el front hacia el rear. Implementar el operador Reubicar(C) que consiga el efecto descrito utilizando, auxiliarmente, una cola de prioridad. 20. Utilizando las operaciones definidas sobre el TAD heap, implementar el método de ordenamiento Heapsort(A,m) que ordena, ascendentemente, un arreglo A de m números naturales. Héctor Pincheira Conejeros Estructuras de Datos 4 21. Implementar, recursivamente, el operador Impares(H) que elimina todos los elementos de ordinalidad par existentes en un heap H de números naturales, es decir, los elementos segundo, cuarto, sexto, etc. 22. Se tiene un heap H cuyos elementos obedecen a la definición Type Prioridad = 1..100; Implementar, recursivamente, el operador Kaesimo(H,k) que elimina y retorna el elemento de k-ésima prioridad contenido en H, sin afectar la presencia de los restantes elementos. Si H tiene menos de k elementos, se deberá retornar el valor cero. Por ejemplo si H = 7 ( 21 ( 87 , 74 ) , 43 ( 62 , 59 ) ) entonces el elemento de prioridad k = 4 es 59. 23. Definición: un heapabb es, al mismo tiempo, tanto un heap como un ABB, cuyos nodos pueden contener llaves repetidas. Establecer todas las condiciones necesarias para que un heapabb completo cumpla con la definición. 24. Tres conjuntos de números enteros P, Q, R, se representan mediante tres ABB’s. Implementar el operador Inter(P,Q,R) que genere un conjunto R (inicialmente vacío) con los valores pertenecientes a la intersección entre P y Q, sin que éstos sufran modificación alguna. 25. Se dispone de dos ABB’s T1, T2. Implementar el operador Unir(T1,T2) que, sin recorrer T2, elimine cada uno de sus elementos y los inserte en T1. Finalizada la operación, T2 deberá encontrarse vacío. 26. Implementar el operador Equipesado(T) que determina si un ABB T está o no balanceado por peso. 27. Implementar el operador Avl(T) que determina si un ABB T es o no un AVL. 28. Se dispone de un árbol AVL T completo no vacío. Implementar el operador Separar(T,T1,T2) que genere dos árboles AVL: T1 con los nodos del subárbol izquierdo de T y T2 con los nodos restantes. No se permite la creación de nodos y, finalmente, T deberá quedar vacío. 29. Se dispone de un ABB AVL R, de altura superior a 1000, en el cual la altura del subárbol izquierdo es mayor que la altura del subárbol derecho. Suponer que se agrega un nodo en el subárbol izquierdo de modo tal que su altura aumenta. Según este planteamiento, implementar los operadores: a) Simple+(R) correspondiente a una rotación simple positiva sobre R. b) Doble+(R) correspondiente a una rotación doble positiva sobre R. 30. Implementar el operador Convierte(T,h) que transforma un árbol binario completo T de altura h, en un árbol de Fibonacci de la misma altura. Héctor Pincheira Conejeros Estructuras de Datos 5 31. Implementar el operador Vacia(T) que determina eficientemente si una tabla de hashing abierto T (de longitud M) se encuentra o no vacía. 32. Implementar, recursivamente, el operador Llena(T,k) que determina eficientemente si una tabla de hashing cerrado T (de longitud M) se encuentra o no llena. 33. Una implementación alternativa de hashing abierto consiste en usar una tabla tridimensional de acceso determinado por índices i, j, k, donde i = f(x), j = g(x) y k = h(x) (i = 0.. N-1, j = 0..M-1, k = 0..L-1) con y f, g, h funciones de hashing. Implementar el operador Insertar(T,x) que agrega una llave x en una tabla T tridimensional de hashing abierto. 34. Con el propósito de garantizar la integridad de la información contenida en una tabla de hashing abierto T, periódicamente se efectúa una redistribución de las clases avanzándolas k posiciones. a) Escribir una función de hashing que represente el desplazamiento indicado. b) Implementar el operador (recursivo in-situ) Redistribuir, que consiga el efecto descrito. 35. La definición typedef struct { Clave key; Base info; } Elemento; typedef Elemento Vector[100]; se utiliza para declarar un arreglo de n registros a ser utilizados en el proceso de actualización de un diccionario. Implementar los operadores a) Poner(D,V,n), que agrega el contenido del arreglo V en el diccionario D. b) Sacar(D,V,n), que elimina el contenido del arreglo V del diccionario D. 36. Al otorgar una identificación unívoca a los usuarios de un servicio, cuyos datos están contenidos en un diccionario D, se cometió el error de asignar el mismo código x a n personas diferentes. Por esta razón, se desea otorgar un nuevo código a los afectados. Utilizando la función NewCode(x) la cual, cada vez que se invoca con un código retorna uno nuevo y distinto a los anteriormente generados, se pide implementar, en C++, el operador recursivo Modify, cuyo propósito es concretar la modificación de códigos repetidos. Héctor Pincheira Conejeros