Este documento discute heaps binomiais, que são estruturas de dados eficientes para implementar filas de prioridade. Ele descreve a representação de heaps binomiais como conjuntos de árvores binomiais e apresenta algoritmos para criar, inserir, unir, remover e decrementar elementos em heaps binomiais. Finalmente, discute aplicações como escalonamento por prioridades.
Verbos da Língua Inglesa - Observações preliminares
Heaps Binomiais
1. CENTRO UNIVERSITÁRIO DA FEI
ORLANDO DA SILVA JUNIOR
HEAPS BINOMIAIS
São Bernardo do Campo
2010
2. 2
SUMÁRIO
1 INTRODUÇÃO ..................................................................................................................... 3
2 REPRESENTAÇÃO DE HEAPS BINOMIAIS ................................................................. 6
3 OPERAÇÕES EM HEAPS BINOMIAIS ........................................................................... 6
3.1 Criação de um heap binomial ........................................................................................... 6
3.2 Inserção de um novo elemento ......................................................................................... 6
3.3 União de dois heaps .......................................................................................................... 7
3.4 Eliminação de um elemento .............................................................................................. 8
3.5 Algoritmo de decremento de chaves ................................................................................. 8
3.6 Algoritmo de extração de chaves ...................................................................................... 9
4 APLICAÇÕES ..................................................................................................................... 10
4.1 Algoritmo de escalonamento por prioridades ................................................................. 10
REFERÊNCIAS ..................................................................................................................... 12
3. 3
1 INTRODUÇÃO
Um heap binominal é uma estrutura de dados computacional do tipo intercalável. Essa
estrutura é formada a partir de um conjunto de árvores binomiais que possuem as seguintes
propriedades (CORMEN ET AL, 2002):
Cada árvore binomial obedece à propriedade de heap mínimo, ou seja, a
chave de um nó é maior ou igual à chave de seu pai; e
Pode existir no máximo uma árvore binomial para cada ordem na
árvore maior.
Em 1978, o francês Jean Vuillemin publicou um artigo que apresentava um eficiente
algoritmo para a manipulação de filas de prioridade, uma estrutura de dados que associa cada
valor contido em um conjunto a uma chave. Através do estudo de árvores binomiais, esse
trabalho permitiu que se conhecesse, anos mais tarde, a estrutura e a aplicação de heaps
binomiais. Sobre filas de prioridade, o pesquisador diz (tradução minha):
Uma fila de prioridades é um conjunto; cada elemento desse conjunto tem um nome,
que é usado para singularmente identificar o elemento, e uma etiqueta, ou
prioridade, elaborada a partir de um conjunto totalmente ordenado. Os elementos
da fila de prioridade podem ser pensados como um serviço de espera de
atendimento, onde o item com a menor etiqueta será sempre o próximo a ser
servido. Pilhas e filas são casos especiais de filas de prioridade (VUILLEMIN,
1978).
4. 4
2 REPRESENTAÇÃO DE HEAPS BINOMIAIS
Por heap binomial entende-se um conjunto de árvores binomiais que satisfazem as
propriedades de heaps binomiais. Apresentadas no capítulo 1, essas propriedades limitam a
forma de representar uma estrutura que agrega muitas outras. As árvores binomiais são
árvores ordenadas recursivamente que apresentam as seguintes propriedades (CORMEN ET
AL, 2002):
Para uma árvore binomial Ak, existem 2k nós;
A altura da árvore é igual a k;
Existem exatamente nós na profundidade i, para i de 0 a k; e
A raiz da árvore tem grau k, o qual é maior que o de qualquer outro nó.
Cada nó de uma árvore binomial possui: uma chave, que é um número representativo
para os algoritmos que operam na árvore; um grau (degree), indicando, numericamente,
quantos filhos tem o nó; e três referências: uma para seu pai, outra para seu filho mais à
esquerda e a última para seu irmão mais à direita. Contudo, vale lembrar que essa regra é
genérica e não vale para todos os tipos de construção de algoritmos. Outras informações
podem (e geralmente fazem) fazer parte do nó.
As árvores binomiais que contém os nós representados na regra geral descrita acima
são armazenadas em uma lista ligada. Comumente chamada de lista de raízes, essa lista
guarda a referência de todas as raízes das árvores que a ela pertencem. O conjunto que agrega
todas essas árvores é o heap binomial em estudo no momento.
Algumas outras considerações sobre árvores binomiais devem ser feitas, tais como:
Se o nó é uma raiz, ele não tem pai;
Se o nó não tem nenhum filho, não há referência para o filho mais à
esquerda;
Se o nó é o filho mais à direita, então o nó não tem irmão.
A Figura 1 apresentada abaixo exemplifica as regras definidas para a construção de
um heap binomial.
5. 5
Figura 1 - Exemplo de um heap binomial
Fonte: Autor “adaptado de” Stergiopoulos
A partir de uma sequência de elementos, o algoritmo montará as árvores binomiais
recursivamente. Observando uma das árvores do heap da Figura 1, nota-se que os números
estão dispostos de modo crescente na visão top-down. Esta ordenação visa criar uma estrutura
que permita que uma série de operações básicas utilizadas no desenvolvimento de estruturas
de dados possam ser aplicadas sobre ela.
6. 6
3 OPERAÇÕES EM HEAPS BINOMIAIS
Conforme mencionado no final do capítulo 2, a utilização de heaps binomiais condiz
diretamente com algumas operações que são comumente utilizadas em demais estruturas de
dados. Este capítulo apresentará as principais operações sobre heaps binomiais, bem como os
principais algoritmos que influem diretamente sobre essa operações.
Vale lembrar que para toda e qualquer estrutura de dados as operações de inserção,
remoção e busca são as consideradas essenciais. Todos os demais algoritmos apresentados
neste capítulo fazem parte dessas três operações básicas.
3.1 Criação de um heap binomial
Para que as operações de inserção, busca e remoção possam ser estudadas e, na
verdade, aplicadas sobre um heap binomial, é necessário que antes ele seja criado.
Porque essa operação define apenas uma cabeça para o heap e a inicializa, seu custo é
igual a Θ(1).
3.2 Inserção de um novo elemento
Inserir um novo elemento numa estrutura de heap binomial significa adicionar um
novo nó a essa estrutura. Esta operação básica trabalha com alguns algoritmos internos que
auxiliam na entrada correta do nó e na reestruturação do heap. Esses algoritmos serão
descritos neste e nos seguintes sub-capítulos.
Para que um novo nó possa ser acrescentado a um heap já existente, considerar-se-á
aqui que esse nó já esteja alocado e sua chave já possua um valor.
As etapas para a inserção de um elemento em um heap binomial são:
1. Alocar um nó (heap) temporário;
2. Definir como zero o grau desse nó;
3. Atribuir o nó que se deseja inserir à cabeça do nó temporário; e
4. Unir o heap temporário ao heap binomial principal.
A última etapa descrita acima merece uma explicação detalhada de seu
funcionamento, já que é o cerne do algoritmo de inserção.
7. 7
3.3 União de dois heaps
Como visto anteriormente, o procedimento que une dois heaps é o mais importante do
algoritmo de inserção. Assim como inserir um novo nó envolve mais de um algoritmo, o
processo de união de dois heaps está centrado nos algoritmo de ligação e união de nós.
O algoritmo de ligação trata dois nós de modo que um deles seja a nova cabeça do
outro. Para que esse algoritmo funcione sem perda de referências e dados, todos os nós que
utilizarem-no obrigatoriamente satisfarão as propriedades de heaps binomiais, descritas no
Capítulo 2.
Dado dois nós, A e B, a ligação entre eles – ou seja, A é a cabeça dos filhos de B –
seguirá, a Ο(1), as etapas abaixo.
A cabeça de A recebe o nó B;
O filho de B é o novo irmão de A;
O filho de B é A;
É somado um ao grau de B.
A Listagem 1 apresenta o pseudocódigo do algoritmo.
Listagem 1 - Pseudocódigo de ligação de heaps
O algoritmo de união de nós tem a função de intercalar as listas de raízes de dois nós
em uma única lista ligada que é ordenada por grau em ordem monotonicamente crescente
(CORMEN, 2002). Por realizar a junção de duas listas de modo intercalado, esse algoritmo
assemelha-se ao popular algoritmo de ordenação MergeSort.
Em suma, o algoritmo de união de heaps, primeiramente, junta dois heaps que atentam
às propriedades de heaps binomiais e percorre a lista dessa junção enquanto todos os
elementos não estiverem intercalados e ordenados segundo a definição de heap binomial. Em
outras palavras, o algoritmo de união de heaps é executado para garantir que a estrutura não
perca suas propriedades que a definem como heap binomial.
8. 8
Neste momento, vale comentar que a estrutura de dado estudada neste trabalho utiliza
menos armazenamento de dados que outras estruturas, como árvores AVL, 2-3 e leftist.
Todavia, por uma análise técnica, é possível notar que as operações básicas programadas
dessas árvores se equiparam no pior caso com as mesmas operações do heap estudado
(VUILLEMIN, 1978).
3.4 Eliminação de um elemento
Sendo a chave a identificação do nó de uma estrutura, eliminar um elemento dessa
mesma estrutura significa eliminar sua chave.
Para que a chave seja removida, o nó que dela é proprietário deverá ter apenas uma
chave mínima em toda a sua sub-árvore, ou seja, o algoritmo de eliminação iterará na
estrutura até que todas as chaves da sub-árvore do nó a ser removido estejam atentando às
propriedades de heaps binomiais. Vale lembrar que um nó está de acordo com a definição de
heap binomial quando a chave de sua raiz é menor que de suas folhas.
O algoritmo de eliminação de elementos é composto por duas partes:
1. Algoritmo de diminuição de chaves: decrementa a chave de um nó
agregado em um heap até que essa chave seja igual a passada por
parâmetro; e
2. Algoritmo de extração da chave mínima: busca a raiz com chave
mínima, remove-a da lista de raízes e une o heap a outro criado
temporariamente para apontar para a cabeça do heap principal.
Os próximos dois sub-capítulos demonstrarão e analisarão o funcionamento desses
algoritmos.
3.5 Algoritmo de decremento de chaves
A Listagem 2 apresenta o algoritmo que decrementa a chave de um nó. Inicialmente, é
necessário verificar se a nova chave não é maior que a chave atual. Caso não seja, a chave
antiga é substituída pela nova.
O funcionamento do algoritmo se dá na iteração. Nessa iteração, toda chave é
comparada com a chave de seu pai. Se as chaves forem iguais, a árvore é ordenada como heap
mínimo; senão, a chave principal é trocada com a de seu pai, bem como demais informações.
9. 9
Como a profundidade da árvore é, no máximo, o piso de log2n, o algoritmo demora o
tempo de Ο(log2n).
Listagem 2 - Algoritmo de diminuição de chave
3.6 Algoritmo de extração de chaves
Este algoritmo é um pouco mais simples que o anterior porque já utiliza os demais
algoritmos citados para suas funções elementares.
Para que a chave mínima seja extraída de um nó é necessário que a raiz com esse nó
seja encontrada e alocada temporariamente a fim de que o nó possa ser excluído da lista de
nós do heap. Em seguida, a ordem da lista ligada dos filhos desse nó deverá ser invertida e
atribuída como cabeça de um novo heap temporário. O algoritmo finaliza sua execução com o
algoritmo de união de dois heaps: os heaps principal e temporário são passados para essa
função, que retornará o endereço de memória do novo heap gerando com essa união.
10. 10
4 APLICAÇÕES
Como dito na introdução deste trabalho, o francês Jean Vuillemin desenvolveu o
algoritmo mais eficiente para a manipulação de filas de prioridade. Desta pesquisa, Vuillemin
acabou por descobrir os heaps binomiais. A aplicabilidade de um heap binomial está
intimamente ligada com a utilização de heaps genéricos. Sendo o heap binomial uma estrutura
particular do heap genérico, supõe-se que as aplicações deste valham para aquele. Contudo, o
modo inverso pode não ser percebido, já que o heap particular binomial é a forma mais
objetivada de se processar um problema específico.
Heaps genéricos são utilizados em algoritmos de inserção e ordenação de listas. Nesta
última operação, o clássico algoritmo HeapSort utiliza a estrutura de heap para ordenar os
elementos de uma lista de modo que ela fique em ordem crescente (ou descrescente, conforme
o desejado). Segundo Feofiloff (2010), o segredo do funcionamento desse algoritmo é o heap.
Além de o heap binomial desenvolvido por Vuillemin possuir quase todas as
aplicações do heap genérico, ele também é utilizado, como uma forma opcional, nos
algoritmos de escalonamento de processos round-robin (IOANNOU; KATEVENIS, 2006) e
fila de prioridades (TANEMBAUM, 2010) e na gerência de agendamento de redes de alta
velocidade (IOANNOU; KATEVENIS, 2006). Ademais, os heaps binomiais deram origem a
outra estrutura semelhante, os heaps de Fibonacci (FREDMAN, 2004), deveras conhecida na
ciência da computação por ser utilizada nos algoritmos de Prim, Kruskal, Cheriton-Tarjan e
Dijkstra.
4.1 Algoritmo de escalonamento por prioridades
O escalonamento por prioridades é um algoritmo utilizado na área de Sistemas
Operacionais interativos para auxiliar a CPU no gerenciamento dos processos em execução. A
ideia do escalonamento por prioridades é atribuir uma prioridade a cada processo,
privilegiando o processo com a maior prioridade o uso da CPU.
Basicamente, a cada tique do relógio do computador, o algoritmo reduz a prioridade
de um processo até que ele seja executado por completo ou possa dar lugar a outro processo
cuja prioridade é maior. Em termos de programação, esse algoritmo pode utilizar um heap
binomial para gerenciar a fila de processos para o processador. Com a definição e as
propriedades de heaps binomiais definidas no capítulo 1, o algoritmo de escalonamento por
12. 12
REFERÊNCIAS
CORMEN, Thomas et al. Algoritmos: teoria e prática. Rio de Janeiro: Elsevier, 2002. cap.
19. pag. 111, 365-280.
FEOFILOFF, Paulo. Heapsort. In: Projeto de Algoritmos em C. Disponível em:
<http://www.ime.usp.br/~pf/algoritmos/aulas/hpsrt.html>. Acesso em: 26 abr 2010.
FREDMAN, Michael L. Binomial, Fibonacci, and Pairing Heaps. In: MEHTA, Dinesh P. ;
SAHNI, Sartaj (Org.). Handbook of data structures and applications. USA: Chapman &
Hall/CRC, 2004.
IOANNOU, Anggelos; KATEVENIS, Manolis. Pipelined Heap (Priority Queue)
Management for Advanced Scheduling in HighSpeed Networks. Crete [2006] Disponível
em: <http://archvlsi.ics.forth.gr/muqpro/pipeHeap_ioan_icc01.pdf>
MOH, Christopher. Application of Data Structures: notas de aula. MIT: Massachusetts,
2005. Disponível em:
<http://www.comp.nus.edu.sg/~tantc/ioi_training/2005/ApplicationofDataStructures.ppt>.
Acesso em: 26 abr 2010.
STERGIOPOULOS, Sotirios. Binomial Heap. In: MIRZAIAN, Andy (Org.). Algorithmics
Animation Workshop. Disponível em:
<http://www.cse.yorku.ca/~aaw/Sotirios/BinomialHeap.html>. Acesso em: 18 abr 2010.
TANEMBAUM, Andrew. Sistemas Operacionais Modernos. Pearson Prentice Hall: São
Paulo, 2010. 3. ed.
VUILLEMIN, JEAN. A data structure for manipulating priority queues. Communications
of the ACM, 21(4), 1978.