Análisis y Diseño de Algoritmos Ordenamiento – Heapsort y Quicksort DR. JESÚS A. GONZÁLEZ BERNAL CIENCIAS COMPUTACIONALES INAOE Heaps 2 Un Heap es una estructura de datos binaria ¡ Un arreglo que representa un árbol binario completo Heaps 3 Cada nodo es un elemento del arreglo ¡ Almacena el valor en el nodo ¡ Árbol completamente lleno, excepto en el último nivel (tal vez) Arreglo representando el heap ¡ 2 atributos ÷ Length[A] à # elementos en el arreglo A ÷ Heap-size[A] à # elementos en el heap almacenados en arreglo A Heap-size ≤ length[A] ¡ Raíz del árbol A[1] ¡ Heaps 4 Dado el índice i de un nodo ¡ Parent(i) à return floor(i/2) ¡ Left(i) à return 2i ¡ Right(i) à return (2i + 1) Propiedad Heap ¡ MAX-HEAP: A[PARENT(i)] ≥ A[i] ¡ MIN-HEAP: A[PARENT(i)] ≤ A[i] Altura de un nodo ¡ # arcos de la ruta simple más larga del nodo a una hoja Altura del árbol ¡ Altura de la raíz ¡ Altura del árbol es Θ(lgn) Heaps 5 Manipulación de heaps ¡ Entrada ÷ Arreglo A y un índice i ÷ Asume que LEFT(i) y RIGHT(i) son heaps ¢ A[i] puede ser menor que sus hijos • Viola la propiedad de un heap (MAX-HEAP) ¡ Heapify ÷ Mueve A[i] en el heap ¢ El subárbol con raíz en el índice i se convierte en un heap Heapify 6 Algoritmo Heapify ¡ Llamada recursiva a Heapify para sus i’s subárboles, si no cumplen con la propiedad del heap Heapify 7 Heap-size[A] à 10 A[2], i = 2 no cumple con la propiedad de un heap (MAX-HEAP) Heapify 8 Heapify 9 Tiempo de ejecución de heapify ¡ En un subárbol de tamaño n, con raíz en el nodo i ÷ ÷ ÷ ÷ Θ(1), relación fija entre A[i], A[LEFT(i)], A[RIGHT(i)] más Tiempo de ejecución de HEAPIFY en un subárbol con raíz en uno de los hijos de i ¢ Tamaño de los subárboles de los hijos es a lo más 2n/3 T(n) ≤ T(2n/3) + Θ(1) ¢ Por el caso 2 del método maestro ¢ T(n) = O(lgn) Alternativa ¢ Caracterizar tiempo de ejecución de HEAPIFY sobre un nodo de altura h como: O(h) Construcción del Heap 10 Uso de heapify para construir el heap ¡ Los elementos A[(floor(n/2) + 1) … n] son hojas del árbol ¡ Orden en que se procesan los nodos garantiza que los subárboles con raíz en los hijos del nodo i son heaps antes de que HEAPIFY se ejecute en dicho nodo Construcción del Heap 11 Algoritmo BuildHeap Construcción del Heap 12 Análisis BuildHeap 13 Cada llamada a HEAPIFY ß O(lgn), hay O(n) llamadas ¡ Cuando mucho O(nlgn) ÷ Cota ¡ superior pero no justa Propiedad: En el n-elemento de un HEAP hay a lo más h +1 n / 2 ⎡ ⎤ nodos de altura h ÷ Tiempo de ejecución de HEAPIFY en nodo altura h à O(h) ÷ Costo de BuildHeap Análisis BuildHeap 14 Del resultado anterior obtenemos el tiempo de ejecución de BuildHeap ⎣lg n ⎦ ⎣lg n ⎦ ⎛ h ⎞ ⎡ n ⎤ ⎜ ⎟ O ( h ) = O n * 1 / 2 ∑ ∑ h ⎟ ⎜ ⎢⎢ 2 h +1 ⎥⎥ h =0 h = 0 2 ⎠ ⎝ ∞ Note que ∑ x k = k =0 1 , para | x | < 1. La derivada de ambos lados, 1− x ∞ d ⎛ ∞ k ⎞ d ⎛ 1 ⎞ 1 k −1 , es igual a kx = . ⎜ ∑ x ⎟ = ⎜ ⎟ ∑ 2 dx ⎝ k =0 ⎠ dx ⎝ 1 − x ⎠ (1 − x) k =0 Multiplicando ambos lados de la equivalencia por x obtenemos : x . 2 (1 − x) h =0 En nuestro caso k = h y x = 1/2. ∞ ∑ kx k = h 1/ 2 = = 2, x = 1 / 2. h 2 2 ( 1 − 1 / 2 ) h =0 Entonces el tiempo de ejecución es O(n *1/2 * 2) = O(n). ∞ Entonces ∑ Algoritmo HeapSort 15 Algoritmo HeapSort 16 Análisis de HeapSort 17 BUILD-HEAP: O(n) for loop: n-1 veces Intercambio de elementos: O(1) MAX-HEAPIFY: O(lgn) Total time: O(nlgn) Una implementación buena de QuickSort es mejor que HeapSort Colas con Prioridades (Priority Queues) 18 Utilizar heaps para implementar colas con prioridades Priority queue Estructura de datos para mantener un conjunto S de elementos ¡ Cada elemento tiene un valor asociado llamado à llave ¡ Operaciones ¡ ÷ Insert(S,x), inserta x en S, S ß S ∪ {x} ÷ Maximum(S), regresa el elemento de S con llave más alta ÷ Extract-Max(S), remueve y regresa el elemento de S con llave más alta Aplicaciones de “Priority Queues” 19 Colas para calendarizar trabajos en una computadora compartida ¡ ¡ Mantiene la pista de los trabajos y sus prioridades Cuando termina un trabajo, inicia el trabajo con más alta prioridad ÷ Extract-Max ¡ Inserta un trabajo en cualquier momento con Insert Operaciones para Priority Queues 20 Tiempo de Ejecuciónpara Priority Queues 21 Heap-Extract-MAX ß O(lgn) Heap-Insert ß O(lgn) Heap-Increase-Key ß O(lgn) QuickSort 22 Sigue estrategia divide y conquista ¡ Divide ÷ Particiona ÷ Cada ÷ En ¡ elemento de A[p…q] ≤ cada elemento de A[q+1…r] esta parte se calcula el índice q Conquista ÷ A[p…q] ¡ A[p…r] en 2 subarreglos, A[p…q] y A[q+1…r] y A[q+1…r] son recursivamente ordenados Combina ÷ No se requiere hacer trabajo en esta parte, A[p…r] queda ordenado Algoritmo QuickSort 23 Algoritmo Partition 24 Existen diferentes algoritmos para particionar Algoritmo Partition 25 Gris Claro: partición menor Gris Oscuro: partición mayor Último elemento: pivote Blanco: Sin asignar Performance de QuickSort 26 Depende de si la partición está o no balanceada ¡ Depende de qué elemento se utilizó para particionar ¡ Partición balanceada ÷ Rápido ¡ como MergeSort Partición no-balanceada ÷ Lento como InsertionSort Performance de QuickSort 27 Peor caso en partición (entrada previamente ordenada) 1 región con n-1 elementos y otra con 1 elemento ¡ Asume que esto sucede en cada paso del algoritmo ¡ Costo de particionar Θ(n), T(1) = Θ(1) ¡ Recurrencia T(n) = T(n-1) + Θ(n) ¡ n = ∑ Θ( k ) k =1 ⎛ n ⎞ = Θ⎜ ∑ k ⎟ ⎝ k =1 ⎠ = Θ( n 2 ) Serie Aritmética n ∑k = k =1 1 n(n + 1) 2 = Θ( n 2 ) Performance de QuickSort 28 Mejor Caso en Partición ¡ Regiones de tamaño n/2 ¡ Recurrencia T(n) = 2T(n/2) + Θ(n) ¡ Caso 2, teorema maestro ß T(n) = Θ(nlgn) Performance de QuickSort 29 Particionamiento balanceado ¡ Caso promedio más cerca del mejor caso que del peor ¡ Suponiendo un split en proporción 9-1ß parece muy desbalanceado ¡ Recurrencia T(n) = T(9n/10) + T(n/10) + n ÷ Vea el árbol de recurrencia en la siguiente lámina Performance de QuickSort 30 Árbol de recurrencia caso de split balanceado Performance de QuickSort 31 Particionamiento balanceado ¡ Cada nivel tiene costo n ¡ Condición frontera a profundidad log10n = Θ(lgn) ÷ Los niveles tienen un costo de cuando mucho n La recursión termina a profundidad log10/9n = Θ(lgn) ¡ Costo total de QuickSort ß = Θ(nlgn) ¡ ÷ Para cada split con proporción constante Performance de QuickSort 32 Intuición para el caso promedio ¡ Partition produce buenos y malos splits ÷ Distribuidos aleatoriamente ÷ Suponga que los buenos y malos alternan (en el árbol) ¢ Bueno à splits del mejor caso ¢ Malo à splits del peor caso ÷ Tiempo de ejecución es todavía Θ(nlgn) ¢ Con una constante más grande escondida por la notación-O Comparación de Algoritmos 33 Algoritmo Peor Caso Caso Promedio Mejor Caso Comentarios InsertionSort Θ(n2) Θ(n2) Θ(n) MergeSort Θ(nlgn) Θ(nlgn) Θ(nlgn) Requiere Memoria HeapSort Θ(nlgn) Θ(nlgn) Θ(nlgn) Constantes Grandes QuickSort Θ(n2) Θ(nlgn) Θ(nlgn) Constantes Pequeñas