Ordenamiento Algoritmos y Estructuras de Datos 2 Departamento de Computación, Facultad de Ciencias Exactas y Naturales, Universidad de Buenos Aires Mayo de 2015 Funciones auxiliares unirListasDeArreglo(in A: arreglo(lista(alumno)), out B : arreglo(alumno)) lista(α) B ← Vacı́a() for i ← 0 to tam(A − 1) do unirListas(B, A[i]) end for O(1) O(|A[i]|) Complejidad del algoritmo: O(1) + O(|A[0]|) + O(|A[1]|) + ... + O(|A[9]|) = O(1) + P9 i=0 O(|A[i]|) = P9 i=0 O(|A[i]|) = O(n) (Como mucho hay n hombres o n mujeres) unirListas(inout IZQ: lista(α), inout DER : lista(α) ) for i ← 0 to Longitud(DER − 1) do AgregarAtras(IZQ, Copiar(A[0])) Fin(DER) end for O(copy(α)) O(1) Complejidad del algoritmo: O(|DER| ∗ |α|) deListaAArreglo(inout A: lista(α), out B : arreglo(α) ) arreglo(α) B ← crearArreglo(Longitud(A)) for i ← 0 to Longitud(A − 1) do B[i] ← A[0] Fin(A) end for O(|A|) O(copy(α)) O(1) Complejidad del algoritmo: O(|A| ∗ |α|) 1 unirArreglos(in A: arreglo(α), in B: arreglo(α), in C: arreglo(α) out R : arreglo(α) ) arreglo(α) B ← crearArreglo(Longitud(A) + Longitud(B) + Longitud(C)) nat idx ← 0 for i ← 0 to Longitud(A − 1) do R[idx] ← A[i] idx ← idx + 1 end for for i ← 0 to Longitud(B − 1) do R[idx] ← B[i] idx ← idx + 1 end for for i ← 0 to Longitud(C − 1) do R[idx] ← C[i] idx ← idx + 1 end for Complejidad del algoritmo: O((|A| + |B| + |C|) ∗ copy(α)) 2 O(|A| + |B| + |C|) O(1) O(copy(α)) O(copy(α)) O(copy(α)) Primer ejercicio Considere la siguiente estructura para guardar las notas de un alumno de un curso: alumno es tuplahnombre: string, sexo: FM, puntaje: Notai donde FM es enum{masc, f em} y Nota es un nat no mayor que 10. Se necesita ordenar un arreglo(alumno) de forma tal que todas las mujeres aparezcan al inicio de la tabla según un orden creciente de notas y todos los varones aparezcan al final de la tabla también ordenados de manera creciente respecto de su puntaje, como muestra en el siguiente ejemplo: Entrada Ana F Juan M Rita F P aula F Jose M P edro M Salida Rita F 6 P aula F 7 Ana F 10 Juan M 6 Jose M 7 P edro M 8 10 6 6 7 7 8 Proponer un algoritmo de ordenamiento ordenaPlanilla(inout p: arreglo(alumno)) para resolver el problema descripto anteriormente y cuya complejidad temporal sea O(n) en el peor caso, donde n es la cantidad de elementos del arreglo. Justificar. ordenaPlanilla(inout A: arreglo(alumno)) arreglo(lista(alumno)) F emenino ← crearArreglo(10) arreglo(lista(alumno)) M asculino ← crearArreglo(10) for i ← 0 to 9 do F emenino[i] ← Vacı́a() M asculino[i] ← Vacı́a() end for for i ← 0 to tam(A − 1) do if A[i].sexo = F then AgregarAtras(F emenino[A[i].nota − 1], A[i]) else AgregarAtras(M asculino[A[i].nota − 1], A[i]) end if end for lista(alumno) M ujeres ← unirListasDeArreglo(F emenino) lista(alumno) Hombres ← unirListasDeArreglo(M asculino) A ← deListaAArreglo(unirListas(M ujeres, Hombres)) Complejidad del algoritmo: O(n) + O(n) + O(n) + O(n) = O(n) donde n = |A|. 3 O(10) = O(1) O(10) = O(1) O(1) O(1) O(1) O(1) O(n) O(n) O(n) Segundo ejercicio Se desea ordenar los datos generados por un sensor industrial que monitorea la presencia de una sustancia en un proceso quı́mico. Cada una de estas mediciones es un √ número entero positivo. Dada la naturaleza del proceso se sabe que, dada una secuencia de n mediciones, a lo sumo b nc valores están fuera del rango [20, 40]. Proponer un algoritmo O(n) que permita ordenar ascendentemente una secuencia de mediciones y justificar la complejidad del algoritmo propuesto. ordenarDatos(inout A: arreglo(nat)) lista(nat) menoresA20 ← Vacı́a() lista(nat) entre20Y 40 ← Vacı́a() lista(nat) mayoresA40 ← Vacı́a() for i ← 0 to tam(A − 1) do if A[i] < 20 then AgregarOrdenado(menoresA20, A[i]) else if A[i] < 41 then AgregarAtras(entre20Y 40, A[i]) else AgregarOrdenado(mayoresA40, A[i]) end if end for arreglo(nat) X ← deListaAArreglo(entre20y40) CountingSort(X) arreglo(nat) Xmen ← deListaAArreglo(menoresA20) arreglo(nat) Xmay ← deListaAArreglo(mayoresA40) A ← unirArreglos(Xmen, X, Xmay) O(1) O(1) O(1) √ O( n) = O(|menoresA20|) O(1) √ O( n) = O(|mayoresA40|) O(n) = O(|entre20Y 40|) √ O(n) = O(|X| + 40) O(√n) = O(|menoresA20|) O( n) = O(|mayoresA40|) O(n) √ Los números menores a 20 o mayores a 40 son a lo sumo n. Luego, para calcular la complejidad final del ciclo principal, multiplicamos el costo de cada rama del condicional, √ por la cantidad de veces que entramos a√esa rama. Por n) ejemplo, el costo de procesar un número menor que 20 es O( √ √ √ pero √ sólo voy a tener que hacerlo O( n) veces. Finalmente, la complejidad del ciclo es O( n · n + n · 1 + n · n) = O(n + n + n) = O(n). Podemos aplicar CountingSort en X porque sabemos que los valores de las mediciones en este arreglo están entre 20 y 40. 4 Tercer ejercicio Se tienen k arreglos de naturales A0 , ..., Ak−1 . Cada uno de ellos está ordenado de forma creciente. Además, se sabe que para todo i el arreglo Ai tiene exactamente 2i elementos. Pk−1 Dar un algoritmo que devuelva un arreglo B de n = i=0 2i = 2k − 1 elementos, ordenado crecientemente, de manera que contenga a todos los elementos de los A[i], contando repeticiones. El algoritmo debe tener complejidad O(n). granMerge(in A: arreglo(arreglo(nat)), out B : arreglo(nat) ) arreglo(nat) B ← crearArreglo(0) for i ← 0 to tam(A − 1) do merge(B, B, A) end for O(1) O(2i ) = O(2i + (2i − 1)) = O(tam(B) + tam(A)) El merge utilizado es el especificado en el apunte de algoritmos básicos. El algoritmo tiene complejidad O(n) = O(2k − 1) = O( Pk−1 i=0 2i ) = Pk−1 i=0 O(2i ) que es la suma de las complejidades de cada iésima iteración, calculadas según la complejidad que tiene el merge del apunte. 5