Filas de prioridade e heaps
Heap binário com inserção e remoção logarítmicas, e o heapsort por remoções sucessivas.
Nesta página
Uma fila comum atende por chegada; uma fila de prioridade atende por importância: cada elemento tem uma prioridade e o próximo a sair é sempre o mais prioritário. É a estrutura por trás do agendamento de tarefas, da simulação de eventos e, como vais ver em Desenho de Algoritmos, dos caminhos mais curtos. O heap binário implementa-a em por operação.
O heap binário
Um heap binário é uma árvore binária completa (todos os níveis cheios exceto o último, preenchido da esquerda) com a propriedade de heap: num heap mínimo, cada pai é menor ou igual aos filhos. Consequência imediata: o mínimo está sempre na raiz, acessível em . E por ser completa, guarda-se num vetor sem apontadores: o filho esquerdo do índice está em , o direito em , o pai em .
Inserir põe o valor na primeira posição livre (mantém a forma completa) e sobe-o (sift-up) enquanto for menor que o pai. Remover o mínimo tira a raiz, move o último elemento para a raiz e desce-o (sift-down), trocando sempre com o menor dos filhos. Subir ou descer percorre no máximo a altura, e a altura de uma árvore completa com nós é : tudo .
Construir um heap de 6 valores
Insere por ordem 7, 3, 9, 1, 5, 4 num heap mínimo, mostrando o vetor:
- 7: .
- 3: , sobe (pai 7 maior): .
- 9: , pai 3 menor, fica.
- 1: , pai 7 maior, troca: ; pai 3 maior, troca: .
- 5: , pai 3 menor, fica.
- 4: , pai 9 maior, troca: ; pai 1 menor, fica.
Confirma a propriedade: 1 é menor que 3 e 4; 3 é menor que 7 e 5; 4 é menor que 9. Repara que o vetor não está ordenado: o heap só garante o mínimo no topo e pais antes dos filhos. Confundir heap com vetor ordenado é o erro mais comum desta página.
Heapsort
Se o mínimo está sempre na raiz, ordenar é remover o mínimo vezes. A primeira remoção no heap acima: tira o 1, move o último (9) para a raiz, , e desce o 9 trocando com o menor filho: filhos 3 e 4, troca com 3, ; filhos 9 e 7, troca com 7, . Saiu o 1 e o heap está refeito. As remoções seguintes devolvem 3, 4, 5, 7 e 9, por esta ordem: a saída é .
O heapsort faz exatamente isto no próprio vetor, remoções de cada: tempo garantido (sem o pior caso quadrático do quicksort) e espaço . Paga dois preços: não é estável e tem má localidade (os sift-down saltam pelo vetor), por isso perde para o quicksort e o mergesort na maioria dos dados reais. Usa o heap quando precisas da fila de prioridade viva, não quando precisas de ordenar uma vez.
Construção em tempo linear
Inserir valores um a um custa , mas construir o heap despejando os valores no vetor e aplicando sift-down de baixo para cima custa . A intuição: a maioria dos nós está nos níveis de baixo, onde descer é barato. É um daqueles resultados que parecem errados até somares a série.