TEMA 5 Conceptos de Java para Estructuras de Datos Diseño recursivo y eficiente: soluciones Divide y Vencerás para la ordenación y la selección
EJERCICIOS RESUELTOS
Ejercicio 1.- El método toString de una LEG es el siguiente: public String toString() { String res = ""; NodoLEG aux; for (aux = primero; aux != null; aux = aux.siguiente) aux.siguiente) res += aux.dato.toString() + “\n”; return res; }
Escribe la versión recursiva de este método, planteando el caso base y el caso general. Indica qué tipo de algoritmo recursivo es.
Solución: * Caso base: lista vacía (devuelve la cadena vacía) * Caso general: lista con algún nodo (devuelve la descripción del primer nodo más la descripción del resto, que a su vez es otra lista) public String toString() { return toString(primero); } private String toString(NodoLEG aux) { if (aux == null) return “”; aux.dato.toSt ring() + “\n” + else return aux.dato.toString() toString(aux.siguiente); }
El algoritmo recursivo es lineal no final.
// Caso base // Caso general
Ejercicio 2.- Invertir recursivamente un array. Ejemplo:
A
B
C
D
D
C
B
A
Solución:
public static void invertir(T v[]) { invertir(v, 0, v.length - 1); } private static void invertir(T v[], int inicio, int fin) { if (inicio < fin) { T tmp = v[inicio]; v[inicio] = v[fin]; v[fin] = tmp; invertir(v, inicio + 1, fin – 1); } }
Ejercicio 3.- Función recursiva que devuelva el máximo de un array.
Solución:
public static > T maximo(T v[]) { return maximo(v, 0); } private static > T maximo(T v[], int inicio) { if (inicio == v.length) return null; else { T max = maximo(v, inicio + 1); if (max == null || v[inicio].compareTo(max) > 0) max = v[inicio]; return max; } }
Ejercicio 4.- Calcula la talla, las instancias significativas y la complejidad asintótica para cada una de ellas del siguiente método: public static > void insercionDirecta(T a[]) { for (int i = 1; i < a.length; i++) { T elemAInsertar = a[i]; int posIns = i; for (; posIns > 0 && elemAInsertar.compareTo(a[posIns-1]) < 0; posIns--) { a[posIns] = a[posIns-1]; } a[posIns] = elemAInsertar; } }
Solución: Talla: N = a.length
Instancias significativas: Mejor caso: el vector de entrada está ordenado ascendentemente Peor caso: el vector de entrada está ordenado descendentemente Ecuaciones de coste: M TinsercionDirecta (N) = k 1*N + k 2 P N TinsercionDirecta (N) = k 3 + i=1 (k 4*i + k 5) = k 4*N*(N+1)/2 + k 5*N + k3 Complejidad asintótica: TinsercionDirecta(N) (N) 2 TinsercionDirecta(N) O(N )
Ejercicio 5.- Analiza el coste de los siguientes métodos: a) Búsqueda dicotómica en un array ordenado: private static > E buscar(E x, E v[], int inicio, int fin) { if (inicio > fin) return null; int mitad = (inicio + fin) / 2; int res = v[mitad].compareTo(x); if (res < 0) return buscar(x, v, mitad + 1, fin); if (res > 0) return buscar(x, v, inicio, mitad - 1); return v[mitad]; }
b) Búsqueda dicotómica en una lista ordenada: private static > E buscar(E x, NodoLEG n, int talla) { if (talla <= 0) return null; int mitad = talla / 2; NodoLEG aux = n; for (int i = 0; i < mitad; i++) aux = aux.siguiente; int res = aux.dato.compareTo(x); if (res < 0) return buscar(x, aux.siguiente, talla – mitad - 1); if (res > 0) return buscar(x, n, mitad); return aux.dato; }
Solución: a) Búsqueda dicotómica en un array ordenado: Talla del problema: N = fin – inicio + 1 Sí que hay instancias significativas: o Mejor caso: x está en la posición (inicio + fin)/2 o Peor caso: x no está en el array Ecuaciones de recurrencia: o Mejor caso: T buscar M(N) = k o Peor caso: T buscar P(N=0) = k T buscar P(N>0) = 1* T buscar P(N/2) + k Para la última ecuación aplicamos el teorema 3, con a=1 y c=2: Coste asintótico del método: T buscar (N) (1) T buscar (N) (log2 N)
b) Búsqueda dicotómica en una lista ordenada: Talla del problema: N = talla Sí que hay instancias significativas: o Mejor caso: x está en el nodo que ocupa la posición talla/2 o Peor caso: x no está en la lista Ecuaciones de recurrencia: o Mejor caso: T buscar M(N) = k 1*N + k 2 o Peor caso: T buscar P(N=0) = k T buscar P(N>0) = 1* T buscar P(N/2) + k 1*N + k 2 Para la última ecuación aplicamos el teorema 4, con a=1 y c=2: Coste asintótico del método: T buscar (N) (N) T buscar (N) (N)
Ejercicio 6.- Analiza el coste de los siguientes métodos: a) private static int sumar1(int v[], int ini, int fin) { int suma = 0; if ( ini == fin ) suma = v[ini]; if ( ini < fin ) { suma = v[ini] + v[fin]; suma += sumar1(v, ini+1, fin-1); } return suma; }
b) private static int sumar2(int v[], int ini, int fin) { int suma = 0; if ( ini == fin ) suma = v[ini]; if ( ini < fin ) { int mitad = (fin + ini) / 2; suma = sumar2(v,ini,mitad) + sumar2(v,mitad+1,fin); } return suma; }
Solución: a) Talla del problema: x = fin – ini + 1 No hay instancias significativas pues hay que recorrer todo el vector en
cualquier caso Ecuaciones de recurrencia: Tsumar1(x 1) = k Tsumar1(x > 1) = 1* T sumar1(x – 2) + k Para la última ecuación aplicamos el teorema 1, con a=1 y c=2: Coste asintótico del método: Tsumar1(x) (x)
b) Talla del problema: x = fin – ini + 1 No hay instancias significativas pues hay que recorrer todo el vector en
cualquier caso Ecuaciones de recurrencia: Tsumar2(x 1) = k Tsumar2(x > 1) = 2* T sumar2(x / 2) + k Para la última ecuación aplicamos el teorema 3, con a=2 y c=2: Coste asintótico del método: Tsumar2(x) (x)
Ejercicio 7.- Sea v un vector de componentes Integer positivas que se ajustan al perfil de una curva cóncava, es decir, que existe una única posición k en el vector tal que: Los elementos a la izquierda de k están ordenados descendentemente Los elementos a la derecha de k están ordenados ascendentemente k
Ejemplo:
4
3
2
1
2
3
4
5
6
7
Diseñar el método recursivo que más eficientemente determine dicha posición k . Indica las instancias significativas y analiza el coste del método.
Solución: public static int buscarPosK(Integer v[]) { return buscarPosK(v, 0, v.length - 1); } private static int buscarPosK(Integer v[], int inicio, int fin) { if (inicio > fin) return -1; else { int resAnt = 1, resSig = 1, mitad = (inicio + fin) / 2; if (mitad > inicio) resAnt = v[mitad-1].compareTo(v[mitad]); if (mitad < fin) resSig = v[mitad+1].compareTo(v[mitad]); if (resAnt < 0 && resSig > 0) return buscarPosK(v, inicio, mitad – 1); else if (resAnt > 0 && resSig < 0) return buscarPosK(v, mitad + 1, fin); else return mitad; }
Talla del problema: N = fin – inicio + 1 En la llamada más alta: N = v.length Instancias significativas: Mejor caso: la posición k se encuentra en la posición (inicio + fin) / 2. Peor caso: el vector v está totalmente ordenado. Por lo tanto, k coincide con uno de los extremos del vector Ecuaciones de recurrencia: Mejor caso: T buscarPosK M( N ) = k Peor caso: T buscarPosK P( N =0) = k T buscarPosK P( N >0) = 1 * T buscarPosK P( N /2) + k Coste:
T buscarPosK ( N ) (1) T buscarPosK ( N ) O(log2 N ), aplicando el teorema 3 con a=1 y c=2.
Ejercicio 8.- Una estrategia alternativa a la presentada para diseñar la ordenación por fusión (mergeSort ) es la de dividir el array en tres partes, ordenarlas y después fusionarlas. Analícese su coste temporal e indíquese si mejora el del método mergeSort .
Solución:
Talla del problema: N = fin – inicio + 1 En la llamada más alta: N = v.length Instancias significativas: No hay instancias significativas Ecuaciones de recurrencia: TmergeSort_3( N ≤1) =k 1 TmergeSort_3( N >1) = 3 * T mergeSort_3( N /3) + TmezclaNaturalDyV_3( N ) +k 2 Coste asintótico: TmergeSort_3( N ) ( N *log3 N ) ¿Es más eficiente? No, ya que N *log3 N = N * log2 N / log23, y log 23 es una constante.