1
Ordenamiento, Heapsort y Colas de
prioridad
Agustín J. González
ELO 320: Estructura de Datos y
Algoritmos
2
Ordenamiento y Estadísticas de
Orden
 Problema de ordenamiento:
 Entrada: Una secuencia de n números (a1,a2, ..., an)
 Salida: Una permutación (a1´, a2´, a3´,...,an´) de la
entrada tal que a1´≤ a2´ ≤ a3´ ≤... ≤ an´
 Soluciones:
 Insertion-Sort,
 merge-sort, heapsort, quicksort. ---> Ω(n lg n)
 La estadística de orden i-ésimo de un conjunto de n
números, es el i-ésimo número más pequeño.
 ---> O(n)
3
1
6
1
4
1
0
7 9 38
142
16 |14 |10 | 8 | 7 | 9 | 3 | 2 | 4 | 1
Heapsort
 La estructura de datos “heap” es un arreglo
de objetos que pueden ser vistos como un
árbol binario completo (sólo pueden faltar
nodos al final del último nivel).
 Ej.
1 2 3 4 5 6 7 8 9 10
4
 El arreglo puede contener más entradas (=length(A)) que el heap
(=heap_size(A))
 Obviamente heap_size(A) ≤ length(A)
 La raíz del árbol es A[1] (Con índices partiendo de 1 aquí)
 Dado un índice i de un nodo, su padre, hijo izquierdo e hijo derecho
son determinados como:
 Parent(i)
return  i/2 
 Left(i)
return 2i
 Right(i)
return 2i+1
 Estos procedimientos se pueden implementar como macros o
código “in-line”.
 Propiedad heap : A [Parent(i)]≥ A [i] para todo nodo diferente de la
raíz.
Propiedades del heap
16 |14 |10 | 8 | 7 | 9 | 3 | 2 | 4 | 1
1 2 3 4 5 6 7 8 9 10
5
Heapify(A,i)
le= Feft(i)
ri= Right(i)
if( le <= heap_size(A) && A[le] > A[i] )
largest = le
else
largest = i
if ( ri <= heap_size(A) && A[ri] > A[largest] )
largest = ri
if (largest != i)
exchange A[i] <-> A[largest]
Heapify(A, largest)
Procedimientos básicos usados en algoritmos de
ordenamiento y en colas de prioridad
 Heapify(A,i): Entrada: Left(i) y Right(i) son heaps, pero A[i]
puede ser menor que sus hijos.
 Salida: Heapify mueve A[i] hacia abajo para que el sub-árbol
con raíz i sea un heap.
Θ (1)
O (tamaño
subárbol)
6
2n/3
n/3
T(n) ≤ T(2n/3) + Θ(1)
==> T(n) = O(lg n)
Análisis de tiempo de Ejecución de
Heapify
 T(n) = Θ(1) + Tiempo de Heapify sobre uno
de los sub-árboles.
 El peor caso para el tamaño del sub-árbol es
2n/3. Éste ocurre cuando la última fila está la
mitad llena.
7
Build_Heap(A) {
heap_size [A] = length [A]
for i = length(A) /2  downto 1
do Heapify(A,i)
}
4 | 1 | 3 | 2 | 16 | 9 | 10 | 14 | 8 | 7Ejemplo:
Construcción del heap: Build_Heap
 La construcción del heap se logra aplicando la función heapify
de manera de cubrir el arreglo desde abajo hacia arriba.
 Notar que los nodos hojas, ya son heap. Éstos están en
A[ (n/2+1) .. n].
 El procedimiento Build_Heap va a través de los nodos restantes
y corre heapify en cada uno.
8
 Cada llamado a Heapify tiene un costo O(lgn) y como a lo más
hay n de estos llamados, una cota superior para el costo de
Build_heap es O(nlgn).
 Un mejor análisis conduce a O(n)
 ¿Cuántos subárboles (nodos) de altura h hay como máximo?
 Para subárboles (nodos) de altura h el costo de Heapify es
O(lgn)= O(h).
 Luego el costo es
   






=



∑∑ ==
+
n
h
h
n
h
h
h
nOO(h)
n lg
0
lg
0
1
22
2
)2/11(
2/1
2 2
0
=
−
=∑
∞
=h
h
h
lgn
h 0 0
Build_Heap puede ser acotado por:
n ( *2) ( )
2 2h h
h
h h
O O n O n O n
∞
= =
∴
   
= = =   
  
∑ ∑
n/2h+1

Análisis del tiempo de ejecución
9
Heapsort(A)
Build_Heap(A)
for (i= lenght(A) downto 2 ) do
exchange A[1] <-> A[i]
heap_size [A] = heap_size [A]-1
Heapify(A,1)
Algoritmo Heapsort
 1.- Construir un heap invocando a Build_Heap
 2.- Intercambiar el primer elemento, la raíz y mayor elemento,
del heap con el último.
 3.- Restituir la propiedad heap en el heap de n-1 elementos.
 El costo de Heapsort es O(n lg n) porque el llamado a
Build_Heap toma O(n) y luego tenemos n-1 llamados a Heapify
cuyo tiempo es O(lgn).
10
Colas de Prioridad
 Heapsort es muy bueno, pero es superado por quicksort (lo veremos
luego).
 La aplicación más popular de heapsort es para implementar colas de
prioridad.
 Una cola de prioridad es una estructura de datos que mantiene un
conjunto S de elementos cada uno asociado con una clave key.
 La cola de prioridad soporta las siguientes operaciones:
 Insert(S,x): inserta x en el conjunto S
 Maximum(S): retorna el elemento con mayor clave.
 Extract-Max(S): remueve y retorna el elemento con mayor clave.
 Aplicaciones:
- Itineración de tareas en sistemas de tiempo compartido
- Colas de prioridad en simuladores conducidos por evento (Event-
driven simulator)
11
Tiempo de Heap_Extract_Max : O(lg n) básicamente el tiempo de Heapify
Tiempo de Heap_Insert : O(lg n) porque es el recorrido desde la nueva hoja a la raíz.
Operaciones en Colas de prioridad
 Heap_Maximum(S): retorna el nodo raíz en tiempo θ(1).
 Heap_Extract_Max(A)
if heap_size[A] <1 then error “Heap undeflow”
max = A[1]
A[1] = A[heap_size [A]]
heap_size [A] = heap_size [A]-1
Heapify(A,1)
return max
 Heap_Insert(A, key)
heap_size [A] = heap_size [A]+1
i = heap_size [A]
while (i > 1 and A[Parent(i)] < key) do
A[i] = A[Parent(i)]
i = Parent(i)
A[i] = key
12
Divertimento
 Se trata de colorear 9 vértices de esta estrella.
 Para ello usted debe partir de un vértice no coloreado, avanzar
en línea recta con caminos de largo 2 y podrá colorear el vertice
de llegada.
 ¿Puede usted colorear nueve vértices?
12
1
2

Heap sort2

  • 1.
    1 Ordenamiento, Heapsort yColas de prioridad Agustín J. González ELO 320: Estructura de Datos y Algoritmos
  • 2.
    2 Ordenamiento y Estadísticasde Orden  Problema de ordenamiento:  Entrada: Una secuencia de n números (a1,a2, ..., an)  Salida: Una permutación (a1´, a2´, a3´,...,an´) de la entrada tal que a1´≤ a2´ ≤ a3´ ≤... ≤ an´  Soluciones:  Insertion-Sort,  merge-sort, heapsort, quicksort. ---> Ω(n lg n)  La estadística de orden i-ésimo de un conjunto de n números, es el i-ésimo número más pequeño.  ---> O(n)
  • 3.
    3 1 6 1 4 1 0 7 9 38 142 16|14 |10 | 8 | 7 | 9 | 3 | 2 | 4 | 1 Heapsort  La estructura de datos “heap” es un arreglo de objetos que pueden ser vistos como un árbol binario completo (sólo pueden faltar nodos al final del último nivel).  Ej. 1 2 3 4 5 6 7 8 9 10
  • 4.
    4  El arreglopuede contener más entradas (=length(A)) que el heap (=heap_size(A))  Obviamente heap_size(A) ≤ length(A)  La raíz del árbol es A[1] (Con índices partiendo de 1 aquí)  Dado un índice i de un nodo, su padre, hijo izquierdo e hijo derecho son determinados como:  Parent(i) return  i/2   Left(i) return 2i  Right(i) return 2i+1  Estos procedimientos se pueden implementar como macros o código “in-line”.  Propiedad heap : A [Parent(i)]≥ A [i] para todo nodo diferente de la raíz. Propiedades del heap 16 |14 |10 | 8 | 7 | 9 | 3 | 2 | 4 | 1 1 2 3 4 5 6 7 8 9 10
  • 5.
    5 Heapify(A,i) le= Feft(i) ri= Right(i) if(le <= heap_size(A) && A[le] > A[i] ) largest = le else largest = i if ( ri <= heap_size(A) && A[ri] > A[largest] ) largest = ri if (largest != i) exchange A[i] <-> A[largest] Heapify(A, largest) Procedimientos básicos usados en algoritmos de ordenamiento y en colas de prioridad  Heapify(A,i): Entrada: Left(i) y Right(i) son heaps, pero A[i] puede ser menor que sus hijos.  Salida: Heapify mueve A[i] hacia abajo para que el sub-árbol con raíz i sea un heap. Θ (1) O (tamaño subárbol)
  • 6.
    6 2n/3 n/3 T(n) ≤ T(2n/3)+ Θ(1) ==> T(n) = O(lg n) Análisis de tiempo de Ejecución de Heapify  T(n) = Θ(1) + Tiempo de Heapify sobre uno de los sub-árboles.  El peor caso para el tamaño del sub-árbol es 2n/3. Éste ocurre cuando la última fila está la mitad llena.
  • 7.
    7 Build_Heap(A) { heap_size [A]= length [A] for i = length(A) /2  downto 1 do Heapify(A,i) } 4 | 1 | 3 | 2 | 16 | 9 | 10 | 14 | 8 | 7Ejemplo: Construcción del heap: Build_Heap  La construcción del heap se logra aplicando la función heapify de manera de cubrir el arreglo desde abajo hacia arriba.  Notar que los nodos hojas, ya son heap. Éstos están en A[ (n/2+1) .. n].  El procedimiento Build_Heap va a través de los nodos restantes y corre heapify en cada uno.
  • 8.
    8  Cada llamadoa Heapify tiene un costo O(lgn) y como a lo más hay n de estos llamados, una cota superior para el costo de Build_heap es O(nlgn).  Un mejor análisis conduce a O(n)  ¿Cuántos subárboles (nodos) de altura h hay como máximo?  Para subárboles (nodos) de altura h el costo de Heapify es O(lgn)= O(h).  Luego el costo es           =    ∑∑ == + n h h n h h h nOO(h) n lg 0 lg 0 1 22 2 )2/11( 2/1 2 2 0 = − =∑ ∞ =h h h lgn h 0 0 Build_Heap puede ser acotado por: n ( *2) ( ) 2 2h h h h h O O n O n O n ∞ = = ∴     = = =       ∑ ∑ n/2h+1  Análisis del tiempo de ejecución
  • 9.
    9 Heapsort(A) Build_Heap(A) for (i= lenght(A)downto 2 ) do exchange A[1] <-> A[i] heap_size [A] = heap_size [A]-1 Heapify(A,1) Algoritmo Heapsort  1.- Construir un heap invocando a Build_Heap  2.- Intercambiar el primer elemento, la raíz y mayor elemento, del heap con el último.  3.- Restituir la propiedad heap en el heap de n-1 elementos.  El costo de Heapsort es O(n lg n) porque el llamado a Build_Heap toma O(n) y luego tenemos n-1 llamados a Heapify cuyo tiempo es O(lgn).
  • 10.
    10 Colas de Prioridad Heapsort es muy bueno, pero es superado por quicksort (lo veremos luego).  La aplicación más popular de heapsort es para implementar colas de prioridad.  Una cola de prioridad es una estructura de datos que mantiene un conjunto S de elementos cada uno asociado con una clave key.  La cola de prioridad soporta las siguientes operaciones:  Insert(S,x): inserta x en el conjunto S  Maximum(S): retorna el elemento con mayor clave.  Extract-Max(S): remueve y retorna el elemento con mayor clave.  Aplicaciones: - Itineración de tareas en sistemas de tiempo compartido - Colas de prioridad en simuladores conducidos por evento (Event- driven simulator)
  • 11.
    11 Tiempo de Heap_Extract_Max: O(lg n) básicamente el tiempo de Heapify Tiempo de Heap_Insert : O(lg n) porque es el recorrido desde la nueva hoja a la raíz. Operaciones en Colas de prioridad  Heap_Maximum(S): retorna el nodo raíz en tiempo θ(1).  Heap_Extract_Max(A) if heap_size[A] <1 then error “Heap undeflow” max = A[1] A[1] = A[heap_size [A]] heap_size [A] = heap_size [A]-1 Heapify(A,1) return max  Heap_Insert(A, key) heap_size [A] = heap_size [A]+1 i = heap_size [A] while (i > 1 and A[Parent(i)] < key) do A[i] = A[Parent(i)] i = Parent(i) A[i] = key
  • 12.
    12 Divertimento  Se tratade colorear 9 vértices de esta estrella.  Para ello usted debe partir de un vértice no coloreado, avanzar en línea recta con caminos de largo 2 y podrá colorear el vertice de llegada.  ¿Puede usted colorear nueve vértices? 12 1 2