Instituto de Computación. Facultad de Ingeniería. Universidad de la República
Primer Parcial de Programación 2
5 de mayo de 2017
Problema 1 (15 puntos)
Considere la siguiente definición de nodos de listas dinámicas de enteros:
s t r u c t n o d o L i s t a { int d a t o ;
n o d o L i s t a * sig ; };
t y p e d e f n o d o L i s t a * L i s t a ;
(a) Defina una función iterativa (no recursiva)mayor que dada una lista de enteros L no vacía y sin ele- mentos repetidos retorne un puntero al nodo de L que contiene al mayor elemento:
L i s t a m a y o r ( L i s t a L ) ;
(b) Defina un procedimiento iterativoordenar que, usando la función mayor, permita ordenar una lista de enteros L, sin elementos repetidos, de mayor a menor sin modificar los punteros de los nodos de L (sólo modificando eventualmente los valores enteros en el campo dato de los nodos). Si L es vacía, el procedimiento no tendrá efecto.
v o i d o r d e n a r ( L i s t a & L ) ;
(c) Calcule el orden de tiempo de ejecución en peor caso de las operaciones mayor y ordenar.
Solución:
(a) Lista mayor ( Lista L ) {
// P u n t e r o al n o d o con el m a y o r e l e m e n t o . L i s t a p m a y o r = L ;
// Por l e t r a la l i s t a L no es vac í a . L i s t a i t e r a d o r = L - > sig ;
w h i l e ( i t e r a d o r != N U L L ) {
if ( i t e r ad o r - > d a t o > pmayor - > d a t o ) p m a y o r = i t e r a d o r ;
i t e r a d o r = i t e r a d o r - > sig ; }
r e t u r n p m a y o r ; }
(b) v o i d o r d e n a r ( L i s t a & L ) {
L i s t a i t e r a d o r = L ; w h i l e ( i t e r a d o r != N U L L ) {
// B u s c o el m a y o r del r e s t o de la l i s t a L i s t a p o s _ m a y o r = m a y o r ( i t e r a d o r ) ;
// H a g o el s w a p del m a y o r con el e l e m e n t o a c t u a l (←- i t e r a d o r )
int aux = i t e r a d o r - > d a t o ;
i t e r a d o r - > d at o = p o s _ m a y o r - > d at o ; p o s _ m a y o r - > d a to = aux ;
// A v a n z o el i t e r a d o r i t e r a d o r = i t e r a d o r - > sig ; }
}
(c) El tiempo de ejecución en el peor caso para la función mayor lo podemos escribir como (ignorando las constantes):
T (n) =
n
X
i=1
c,
donde c es una constante que corresponde a las instrucciones dentro del while y n es la cantidad de elementos de la lista. Entonces, obtenemos que T (n) = n · c. Por lo tanto el orden de ejecución es O(n).
En la función ordenar podemos ver que dentro del while lo que se hace es llamar a la función mayor cada vez con una lista de tamaño menor. Entonces podemos escribir el tiempo de ejecución de la función ordenar como:
T (n) =
n
X
i=1
T0(i)
donde T0es el tiempo de ejecución de la función mayor. Desarrollando obtenemos:
T (n) =
n
X
i=1
c · i
= c n(n + 1) 2
= c 2n2+c
2n.
Entonces el orden de ejecución de la función ordenar es O(n2).
Problema 2 (13 puntos)
Considere la siguiente declaración del tipo de los nodos de un Árbol Binario de Búsqueda (ABB) de enteros:
s t r u c t n o d o A B B { int i n f o ; n o d o A B B * izq ; n o d o A B B * der ; };
t y p e d e f n o d o A B B * ABB ;
(a) Defina una función aplanar que dado un ABB A retorne una lista ordenada de menor a mayor que contenga a todos los elementos de A. Si el árbol es vacío, la función debe retornar la lista vacía. Use la definición de listas de enteros dada en el Problema1.
L i s t a a p l a n a r ( ABB A ) ;
No use operaciones auxiliares propias en la implementación de aplanar, excepto el siguiente procedi- miento concatenar, que puede asumir implementado:
v o i d c o n c a t e n a r ( L i s t a & L1 , L i s t a L2 ) ;
concatenar(L1, L2) agrega los elementos de L2 al final de L1, en el mismo orden (L1 = L1++L2).
(b) ¿Cuál es el orden de tiempo de ejecución en el peor caso de la función aplanar, asumiendo que concatenar(L1, L2) tiene O(n) en el peor caso, siendo n la suma de los largos de L1 y L2? Justifique muy brevemente.
Solución:
(a) Hay dos casos: el árbol es vacío o no. En caso de que el árbol sea vacío debemos devolver una lista vacía, en caso contrario debemos devolver una lista que contenga tenga los elementos del subárbol izquierdo aplanado, el elemento de la raíz y los elementos del subárbol derecho aplanado.
L i s t a a p l a n a r ( ABB A ) {
L i s t a a p l a n a d o I = N U L L ; // l i s t a r e s u l t a d o if ( A != N U L L )
{
// s u b a r b o l izq a p l a n a d o a p l a n a d o I = a p l a n a r ( A - > izq ) ; // s u b a r b o l der a p l a n a d o
L i s t a a p l a n a d o D = a p l a n a r ( A - > der ) ; // n o d o p a r a e l e m e n t o r ai z
L i s t a m e d i o = new n o d o L i s t a ; medio - > d a t o = A - > i n f o ;
// n o d o con e l e m e n t o r a iz se c o l o c a al p r i n c i p i o // de la l i s t a con s u b a r b o l der a p l a n a d o
medio - > sig = a p l a n a d o D ;
c o n c a t e n a r ( a p l a n a d o I , m e d i o ) ; }
r e t u r n a p l a n a d o I ; }
(b) ¿Cuál es el orden de tiempo de ejecución en el peor caso de la función aplanar? Justifique muy brevemente.
La función aplanar consiste en una recorrida en postorden del árbol A que es parámetro de en- trada. Por lo tanto el tiempo de ejecución en el peor caso de aplanar se puede calcular como la suma de las acciones en cada nodo.
En la recorrida, para cada nodo del árbol A se hace una cantidad constante de operaciones (crea- ción de un nuevo nodo, asignación de valores, punteros, etc.) y una invocación al procedimiento concatenar que sabemos que opera sobre listas que en todos los casos tienen una suma de elementos menor a n, donde n es el numéro total de nodos del árbol.
Dado que el el tiempo de ejecución en el peor caso de concatenar es O(m) en el peor caso, siendo m la suma de los largos de las listas que se concatenan, se puede deducir que concatenar en todos los nodos del árbol tiene orden de ejecución en el peor caso menor que n siendo n el total de nodos de A (porque se concatenan listas cuyo largo total siempre es menor o igual a n):
T (n) ≤
n
X
i=1
(c + Tconcatenar(n))
T (n) ≤
n
X
i=1
c0· n T (n) ≤ c0· n2. T (n) es O(n2).
Problema 3 (12 puntos)
Considere la siguiente definición en C/C++ del tipo ArbGen de árboles generales o finitarios de enteros representados con árboles binarios con la semántica: primer hijo (pH) – siguiente hermano (sH):
s t r u c t n o d o A r b G e n { int d a t o ;
n o d o A r b G e n * pH ; n o d o A r b G e n * sH ; };
t y p e d e f n o d o A r b G e n * A r b G e n ;
Defina una función recursiva copiaParcial en C/C++ que dados un árbol A de tipo ArbGen y un entero positivo k, retorne una copia de A, sin compartir memoria con éste, con todos los nodos que están en un nivel menor o igual a k. Si A es vacío o k es cero, el resultado debe ser el árbol vacío. Tenga en cuenta que en un árbol no vacío la raíz está en el nivel 1 y asuma que A->sH es NULL. No use operaciones auxiliares propias en la implementación de copiaParcial.
A r b G e n c o p i a P a r c i a l ( A r b G e n A , u n s i g n e d int k ) ; Ejemplo:
Entrada Resultado
1
2
5 6
3
7
8
4 A
k = 2
1
2 3 4
Solución:
A r b G e n c o p i a P a r c i a l ( A r b G e n a , u n s i g n e d int k ) {
// C o n t r o l o que no h a y a l l e g a d o al l i m i t e de k y que el a r b o l ←- no sea v a c i o
if ( k > 0 && a != NU L L ) {
// C o p i o el n o d o a c t u a l
A r b G e n c o p i a N o d o = new n o d o A r b G e n ; c o p i a N o d o - > d at o = a - > d a t o ;
// H a g o una c o p i a p a r c i a l de los h i j o s del n o d o a c t u a l con ←- k -1 , d a d o que b a j o al s i g u i e n t e n i v e l
c o p i a N o d o - > pH = c o p i a P a r c i a l ( a - > pH , k -1) ;
// H a g o una c o p i a p a r c i a l de los h e r m a n o s del n o d o a c t u a l ←- m a n t e n i e n d o el v a l o r de k d a d o que no e s t o y b a j a n d o de ←- n i v e l
c o p i a N o d o - > sH = c o p i a P a r c i a l ( a - > sH , k ) ; r e t u r n c o p i a N o d o ;
}
r e t u r n N U L L ; }