Você está na página 1de 9

Algoritmo Heapsort

Page 1 of 9

Projeto de Algoritmos
Home | Prefcio | Livros | Stios WWW | ndice

Heapsort
Esta pgina examina um algoritmo sofisticado que rearranja um vetor dado em ordem crescente. Para que a descrio do algoritmo fique mais simples, convm que os ndices do vetor sejam 1..n e no 0..n-1, como usual em C. Resumindo, nosso algoritmo rearranja os elementos de um vetor v[1..n] de tal modo que ele fique em ordem crescente, ou seja, de tal modo que tenhamos v[1] v[2] . . . v[n]. O algoritmo, conhecido como Heapsort, foi inventado por J.W.J. Williams ["Algorithm 232 (heapsort)", Communications of the ACM, 7, p.347-348, 1964]. Veja o verbete Heapsort na Wikipedia. Veja tambm o captulo 14 do "Programming Pearls".

Heap (rvore binria quase completa)


O segredo do funcionamento do algoritmo uma estrutura de dados conhecida como heap (= monte). Um max-heap um vetor v[1..m] tal que [estou escrevendo m e no n de propsito]:
v[f/2] v[f]

para f = 2, . . . , m. Aqui, como no resto desta pgina, vamos convencionar que as expresses que figuram como ndices de um vetor so sempre inteiras. Uma expresso da forma "f/2" significa f/2 , ou seja, o piso de f/2, isto , a parte inteira do quociente de f por 2. Assim, se v[1..17] um max-heap ento, em particular, v[5] v[10] e v[5] v[11]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

999 999 999 999 999 999 999 999 999 999 999 999 999 999 999 999 999 Estranha essa definio de max-heap, no? Talvez a coisa fique mais clara se encararmos a sequncia de ndices 1..m como um rvore binria:
l l l

o ndice 1 a raiz da rvore; o pai de um ndice f f/2 ( claro que 1 no tem pai); o filho esquerdo de um ndice p 2p e o filho direito 2p+1 ( claro que o filho esquerdo s existe se 2p m e o filho direito s existe se 2p+1 m).

A figura abaixo procura desenhar um vetor v[1..55] de modo que cada filho fique na "camada" imediatamente inferior do pai. O vetor definido por v[i]=i e portanto longe est de ser um max-heap. Observe que cada "camada", exceto a ltima, tem duas vezes mais elementos que a "camada" anterior. Com isso, o nmero de "camadas" de v[1..m] exatamente 1 + lg(m), sendo lg(m) o piso de log2m.
1 2 4 5 8 9 10 11 16 17 18 19 20 21 22 23 3 6 7 12 13 14 15 24 25 26 27 28 29 30 31

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 2 of 9

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

Uma vez entendido o conceito de pai e filho, podemos dizer que um vetor um max-heap se o valor de todo pai maior ou igual que o valor de qualquer de seus dois filhos (onde o valor de um ndice p v[p]).

Exerccios
1. Mostre que todo vetor decrescente indexado por 1,2, . . . um max-heap. Mostre que a recproca no verdadeira. 2. O vetor abaixo um max-heap?
161 41 101 141 71 91 31 21 81 17 16

3. Escreva uma funo que decida se um vetor v[1..m] ou no um max-heap. 4. Mostre que v[1..m] um max-heap se e somente se para cada ndice p tem-se a. se 2p m ento v[p] v[2p]; b. se 2p+1 m ento v[p] v[2p+1]. 5. Suponha que v[1..m] um max-heap com m = 2k-1. Mostre que mais da metade dos elementos do vetor est na ltima "camada" do max-heap, ou seja, em v[2k-1.. 2k1].

A funo peneira
O corao de qualquer algoritmo que manipule um max-heap uma funo que recebe um vetor arbitrrio v[1..m] e um ndice p e faz v[p] "descer" para sua posio "correta". Como se faz isso? A ideia bvia. Se v[p] v[2p] e v[p] v[2p+1] ento no preciso fazer nada. Se v[p] < v[2p] e v[2p] v[2p+1] ento basta trocar v[p] com v[2p] e depois fazer v[2p] "descer" para sua posio "correta". No difcil imaginar o que se deve fazer no terceiro caso. Eis um exemplo com p=1. Cada linha da tabela uma "foto" do vetor no incio de uma iterao. 85 99 99 99 99 85 97 97 98 98 98 98 97 97 85 93 96 96 96 96 95 95 95 95 94 94 94 94 93 93 93 85 92 92 92 92 91 91 91 91 90 90 90 90 89 89 89 89 87 87 87 87 86 86 86 86

Eis uma funo iterativa que faz o servio. A varivel f sempre um filho de p; no incio de cada iterao, f ajustado de modo a ser o filho de maior valor de p.
// Recebe p em 1..m e rearranja o vetor v[1..m] de modo // que o "subvetor" cuja raiz p seja um max-heap.

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 3 of 9

// Supe que os "subvetores" cujas razes so filhos // de p j so max-heaps. void peneira (int p, int m, int v[]) { int f = 2*p, x; while (f <= m) { if (f < m && v[f] < v[f+1]) ++f; // f o filho "mais valioso" de p if (v[f/2] >= v[f]) break; x = v[f/2], v[f/2] = v[f], v[f] = x; f *= 2; } }

A seguinte implementao um pouco melhor, porque faz menos trocas e executa a diviso f/2 uma s vez:
void peneira (int p, int m, int v[]) { int f = 2*p, x = v[p]; while (f <= m) { if (f < m && v[f] < v[f+1]) ++f; if (x >= v[f]) break; v[p] = v[f]; p = f, f = 2*p; } v[p] = x; }

A funo peneira: desempenho


A funo peneira muito rpida. O consumo de tempo proporcional ao nmero de iteraes, e esse numero no passa de
log2m

pois o valor de f pelo menos dobra a cada iterao.

Exerccios
6. A seguinte alternativa para a funo peneira funciona corretamente?
void peneira (int p, int m, int v[]) { int x, f; for (f = 2*p; f <= m; f *= 2) { if (f < m && v[f] < v[f+1]) ++f; p = f/2; if (v[p] >= v[f]) break; x = v[p], v[p] = v[f], v[f] = x; } }

7. Escreva uma verso recursiva da funo peneira. 8. Por que a seguinte implementao de peneira no funciona?
void peneira (int p, int m, int v[]) {

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 4 of 9

int x; int f = 2*p; while (f <= m) { if (v[p] < v[f]) { x = v[p], v[p] = v[f], v[f] = x; p = f; f = 2*p; } else { if (f < m && v[p] < v[f+1]) { x = v[p], v[p] = v[f+1], v[f+1] = x; p = f+1; f = 2*p; } else break; } } }

Por que um heap til?


Por que um max-heap uma estrutura de dados to poderosa? Suponha que v[1..m] um maxheap; ento a. a pergunta "qual o maior elemento de vetor?" pode ser respondida instantaneamente: o maior elemento do vetor v[1]; b. se o valor de v[1] for alterado, o max-heap pode ser restabelecido muito rapidamente: a operao peneira (1,m,v) no demora mais que lg(m) para fazer o servio; c. um vetor v[1..m] arbitrrio pode ser transformado em um max-heap muito rapidamente: o comando
for (p = m/2; p >= 1; --p) peneira (p, m, v);

faz o servio em tempo proporcional a m. ( fcil ver que o consumo de tempo limitado por (m lg(m))/2, pois o tempo gasto em cada uma das m/2 iteraes limitado por lg (m). um pouco mais difcil verificar que o tempo , na verdade, limitado por m apenas.)

Exerccios
9. Mostre que o fragmento de programa abaixo faz no mximo m comparaes entre elementos do vetor.
for (p = m/2; p >= 1; --p) peneira (p, m, v);

10. O fragmento de programa abaixo transforma um vetor arbitrrio v[1..m] em maxheap?


for (p = 1; p <= m/2; ++p) peneira (p, m, v);

11. Critique a seguinte ideia: para transformar um vetor arbitrrio em max-heap, basta coloc-lo em ordem decrescente. 12. Escreva uma funo ff que receba um vetor v e um ndice k tais que v[1..k-1] um max-heap e transforme v[1..k] em max-heap. Sua funo deve fazer no mximo 2 lg(k) comparaes entre elementos do vetor. Agora use ff para construir uma funo que transforme qualquer vetor v[1..m] em max-heap. Sua funo deve fazer no

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 5 of 9

mximo 2 m lg(m) comparaes entre elementos do vetor.

O algoritmo heapsort
No difcil juntar tudo que dissemos acima para obter um algoritmo que coloque v[1..n] em ordem crescente.
// Rearranja os elementos do vetor v[1..n] // de modo que fiquem em ordem crescente void heapsort (int n, int v[]) { int p, m, x; for (p = n/2; p >= 1; --p) peneira (p, n, v); for (m = n; m >= 2; --m) { x = v[1], v[1] = v[m], v[m] = x; peneira (1, m-1, v); } }

O comando for transforma o vetor em um max-heap recorrendo cerca de n/2 vezes funo peneira. Feito isso, temos um processo iterativo controlado pelo segundo for. No incio de cada iterao valem os seguinte invariantes:
l l l l

o vetor v[1..n] uma permutao do vetor original, o vetor v[1..m] um max-heap, v[1..m] v[m+1..n] e o vetor v[m+1..n] est em ordem crescente.

claro que v[1..n] estar em ordem crescente quando m for igual a 1.


m n max-heap crescente 888 777 666 555 444 333 222 111 000 999 999 999 999 999 elementos pequenos elementos grandes 1

Heapsort: desempenho
Quanto tempo o heapsort leva para fazer o servio? O tempo proporcional ao nmero de comparaes entre elementos do vetor, e esse nmero no passa de
3 n log2n ,

mesmo no pior caso. De fato, o primeiro for constri o max-heap inicial e faz no mximo n lg (n) comparaes entre elementos do vetor. (Uma anlise mais cuidadosa revela que o nmero de comparaes no passa de n). O segundo for executa cerca de n chamadas de peneira e cada uma dessas chamadas faz no mximo 2 lg(n) comparaes entre elementos do vetor.

Heapsort: animaes
Veja applets de animao do algoritmo:

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 6 of 9

l l l

Sorting Algorithms Animation by David R. Martin Sorting Algorithms na Universidade de British Columbia Sorting Algorithms, pgina de Pat Morin na Universidade de Carlton, Canad

Exerccios
13. Use o heapsort para ordenar o vetor 16 15 14 13 12 11 10 9 8 7 6 5 4. 14. Suponha que o vetor v[1..n] um max-heap. O seguinte fragmento de cdigo rearranja o vetor em ordem crescente?
for (m = n; m >= 2; --m) { int x = v[1]; for (j = 1; j < m; ++j) v[j] = v[j+1]; v[m] = x; }

15. Implemente um max-heap com ponteiros. Cada clula ter um valor e trs ponteiros: um aponta o pai da clula, outro aponta o filho direito e outro aponta o filho esquerdo. Escreva uma verso da funo peneira para um max-heap implementado com ponteiros. 16. Suponha que v[1..m] um max-heap. Suponha que i < j e v[i] < v[j]. Se os valores de v[i] e v[j] forem trocados, v[1..m] continuar sendo um max-heap? Repita o exerccio sob a hiptese v[i] > v[j]. 17. Escreva uma funo HS que receba um max-heap v[1..n] e rearranje o vetor de modo que ele fique em ordem crescente. Tire proveito de que o vetor dado no arbitrrio. SUGESTO: Digamos que um vetor v[1..m] um quase max-heap se
v[f/2] v[f] para f = 4, . . . , m.

Escreva uma funo H que receba um quase max-heap v[1..m] e transforme-o em um max-heap. (Basta fazer uma verso ligeiramente especializada de peneira.) Use H para escrever HS. 18. Escreva uma funo que rearranje um vetor v[1..n] de modo que ele fique em ordem decrescente. SUGESTO: Adapte a definio de max-heap para o problema em questo. Reescreva a funo peneira.

Uma verso mais rpida do Heapsort


A seguinte verso do Heapsort, um pouco diferente da examinada acima, conhecida com Bottom-Up-Heapsort:
// Rearranja os elementos do vetor v[1..n] // de modo que fiquem em ordem crescente void bottom_up_heapsort (int n, int v[]) { int m, f, max, t; constroi_heap (n, v); for (m = n; m > 1; --m) { max = v[1]; f = 2; while (f <= m) {

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 7 of 9

if (f < m && v[f] < v[f+1]) ++f; v[f/2] = v[f]; f *= 2; } f = f/2; if (f < m) { t = v[f], v[f] = v[m], v[m] = t; while (f > 1 && v[f/2] < v[f]) { t = v[f], v[f] = v[f/2], v[f/2] = t; f = f/2; } } v[m] = max; } }

No comeo de cada iterao do for, o vetor v[1..m] um heap que contm os elementos pequenos e o vetor v[m+1..n] crescente e contm os elementos grandes.
// Recebe um vetor v[1..n] e transforma o vetor em // um max-heap void constroi_heap (int n, int v[]) { int m, f, t; for (m = 1; m < n; ++m) { f = m+1; while (f > 1 && v[f/2] < v[f]) { t = v[f/2], v[f/2] = v[f], v[f] = t; f = f/2; } } }

No incio de cada iterao do for, o vetor v[1..m] um heap. No comeo de cada iterao do while, todos os ndices em 1..m+1 satisfazem a propriedade do max-heap exceto talvez v[f] grande demais para v[f/2]. I. Wegener e J. Stolfi observaram que esta verso Heapsort faz, em mdia, duas vezes menos comparaes entre elementos do vetor que a verso discutida acima. (Mas isso no significa, necessariamente, que ela seja duas vezes mais rpida se levarmos em conta as outras operaes, como as trocas, por exemplo.

Exerccio
19. Verifique que a verso abaixo do bottom_up_heapsort equivalente discutida acima. (Ela um pouco mais rpida pois faz a diviso "f/2" uma s vez e reduz o nmero de trocas):
void bottom_up_heapsort (int n, int v[]) { int m, p, f, max, t; constroi_heap (n, v); for (m = n; m > 1; --m) { max = v[1]; p = 1, f = 2; while (f <= m) { if (f < m && v[f] < v[f+1]) ++f; v[p] = v[f]; p = f, f = 2*p;

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 8 of 9

} f = p; if (f < m) { t = v[m]; while (f > 1 && v[p=f/2] < t) { v[f] = v[p]; f = p; } v[f] = t; } v[m] = max; } } void constroi_heap (int n, int v[]) { int m, p, f, t; for (m = 1; m < n; ++m) { f = m+1; t = v[f]; while (f > 1 && v[p = f/2] < t) { v[f] = v[p]; f = p; } v[f] = t; } }

Veja Heapsort no Cprogramming.com URL of this site: www.ime.usp.br/~pf/algoritmos/ 1998 | Last modified: Mon Jul 4 09:18:49 BRT 2011 Paulo Feofiloff IME-USP

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Algoritmo Heapsort

Page 9 of 9

http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html

17/10/2011

Você também pode gostar