estrutura de dados e algoritmos heaps e listas de …boeres/slides_ed/ed_heap.pdf · 2018. 10....
Post on 14-Oct-2020
1 Views
Preview:
TRANSCRIPT
ESTRUTURA DE DADOS E ALGORITMOS
HEAPS E LISTAS DE PRIORIDADES
Listas de Prioridades
! Em muitas aplicações, dados de uma coleção são acessados por ordem de prioridade
! A prioridade associada a um dado pode ser qualquer coisa:
● tempo, custo, etc, mas precisa ser um escalar
! operações comuns (eficientes)
● Seleção do elemento com maior (ou menor) prioridade
● Remoção do elemento de maior (ou menor) prioridade
● Inserção de um novo elemento
Listas de Prioridades
! Implementação
Operação Lista Lista ordenada Árvore balanceada
Heap
Seleção O(n) O(1) O(log n) O(1)
Inserção O(1) O(n) O(log n) O(log n)
Remoção (do menor)
O(n) O(1) O(log n) O(log n)
Alteração de prioridade
O(n) O(n) O(log n) O(log n)
Construção O(n) O(n log n) O(n log n) O(n)
Heaps - Listas de Prioridades
! A relação entre as chaves pode ser MODELADA por árvores binárias ● mas não é implementado por AB e nem ABB
! Cada nó possui as propriedades ● chave do nó i > chave à esquerda (se houver)
● chave do nó i > chave à direita (se houver)
● ainda não definimos quem está a esquerda e direita
! a raiz contém a chave de maior prioridade ! este é elemento 1 da lista
! Implementação: não como árvores ● heaps são implementados usando vetores ou listas sequenciais
Heaps - Listas de Prioridades
! Dado um nó armazenado no índice i, o índice ● filho esquerdo de i : 2 i
● filho direito de i : 2i + 1
● nó pai de i : i div 2
! A relação entre os elementos
si < s i/2
a b c d e f g h i j k 1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 níveis
Heaps - Listas de Prioridades
! Para armazenar uma árvore de altura h precisamos de um vetor de 2h – 1 elementos (no máximo)
● número de nós de uma árvore cheia de altura h
a b c d e f g h i j k 1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 níveis g f
a
c b
e d
i h k j
Heaps - Listas de Prioridades
Alteração do valor de uma chave (prioridade)
! para manter a propriedade de heap, chaves podem migrar ● para baixo (se ele diminuir de valor)
● para cima (se ele aumentar de valor)
Heaps - Listas de Prioridades
Alteração do valor de uma chave (prioridade)
! Para cada uma dessas situações utiliza-se um algoritmo de migração:
● subir (i, n, S) - migra o nó i para cima no heap S
● descer (i,n,S) - migra o nó i para baixo no heap S
" n o número total de nós da heap
subir() – Alteração de prioridade
! Pai de si – elemento si/2
subir (i, n, S [1 .. n]) {
se i > 1 e S [i/2] < S [i] então {
temp = S[i]; S[i]=S[i/2]; S[i/2]=temp;
Subir (i/2, n, S)
} }
descer() – Alteração de prioridade
descer (i, n, S [1 .. n]) {
se 2i+1 ≤ n e S [2i+1] > S [2i]
então filho = 2i+1;
senão filho = 2i;
se filho ≤ n e S [filho] > S [i] então {
temp = S[i]; S[i]=S[filho]; S[filho]=temp;
descer (filho, n, S)
}
}
Construindo uma lista de prioridades
! o valor na primeira posição, é o maior
construir_heap (S, n){ para i = 2, ..., n faça
subir (i, n, S); }
Construindo uma lista de prioridades
! podemos também arranjar usando o descer
! observamos que os elementos de n/2+ 1 em diante não tem filhos
! então podemos descer de n/2 em direção a 1
arranjar (S, n){ para i = n/2, ..., 1 faça
descer(i, n, S); }
Construindo uma lista de prioridades
arranjar (S, n){ para i = n/2, ..., 1 faça
descer(i, n, S); }
1 2 3 4 5 6 7
i=3 40 37 95 42 23 51 27
descer (i, n, S [1 .. n]) { se 2i+1 ≤ n e S [2i+1] > S [2i] então filho = 2i+1; senão filho = 2i;
se filho ≤ n e S [filho] > S [i] então { temp = S[i]; S[i]=S[filho]; S[filho]=temp; descer (filho, n, S) }
}
Construindo uma lista de prioridades
1 2 3 4 5 6 7
i=3 40 37 95 42 23 51 27
i=2 40 42 95 37 23 51 27
arranjar (S, n){ para i = n/2, ..., 1 faça
descer(i, n, S); }
descer (i, n, S [1 .. n]) { se 2i+1 ≤ n e S [2i+1] > S [2i] então filho = 2i+1; senão filho = 2i;
se filho ≤ n e S [filho] > S [i] então { temp = S[i]; S[i]=S[filho]; S[filho]=temp; descer (filho, n, S) }
}
Construindo uma lista de prioridades
1 2 3 4 5 6 7
i=3 40 37 95 42 23 51 27
i=2 40 42 95 37 23 51 27
i = 1 95 42 40 37 23 51 27
95 42 51 37 23 40 27
arranjar (S, n){ para i = n/2, ..., 1 faça
descer(i, n, S); }
descer (i, n, S [1 .. n]) { se 2i+1 ≤ n e S [2i+1] > S [2i] então filho = 2i+1; senão filho = 2i;
se filho ≤ n e S [filho] > S [i] então { temp = S[i]; S[i]=S[filho]; S[filho]=temp; descer (filho, n, S) }
}
HeapSort
Ordenação
● Construir o heap
" então, o valor na primeira posição, é o de maior valor
● Repetidamente trocar esse valor com o de sua posição ao final da lista m, naquela iteração, acertando o heap
HeapSort
Ordenação
arranjar (S, n); // controi o heap m = n;
enquanto m > 1 fazer { temp = S[1]; S[1]=S[m]; S[m]=temp;
m = m-1; descer (1, m, S);
}
HeapSort
arranjar(S,n); m = n;
enquanto m > 1 fazer { temp = S[1]; S[1]=S[m]; S[m]=temp;
m = m-1; descer (1, m, S)
}
1 2 3 4 5 6 7
arranjar 95 42 51 37 23 40 27
m=7 27 42 51 37 23 40 95
descer 51 42 27 37 23 40 95
51 42 40 37 23 27 95
m=6 27 42 40 37 23 51 95
....
Inserção em Heap
! Para inserir nova chave em um heap H:
● inserir um novo valor na posição n + 1 do heap
● chamar subir(): acha a posição apropriada do novo valor x em relação aos valores já existentes no heap H
Proc inserir (x, n, H [1 ... TMAX]) { if ((n+1) <= TMAX) n ← n + 1; H [n] ← x; subir (n, n, H);
}
Remoção em um Heap
! substituir a raiz H[1] por H[n]
! Chamar descer()
! Obs.: em heap, remoção é da chave de maior prioridade
proc Remover (n, H [1 .. n]) { H [1] ← H [n] n ← n – 1 Descer (1, n, H)
}
Construção de Heap
! Se queremos construir um heap com n elementos, podemos recorrer a um algoritmo ingênuo, isto é, inserir os n elementos um a um num heap inicialmente vazio
● procedimento inserir()
! Complexidade O(n log n)
● Por que?
Construção de Heap
! Tem como melhorar a construção?
! O que fazer?
Construção de Heap
! Entretanto, pode-se construir o heap em O(n) pois:
● As folhas da árvore não têm descendentes e portanto já estão em seus lugares na definição de heap (em relação aos filhos inexistentes)
" são os elementos H [n/2 + 1] até H[n])
● Somente os nós internos devem ser colocados em suas posições em relação a seus descendentes com descer()
" elementos H [1] até H[n/2]
● É preciso trabalhar de trás para frente desde n/2 até 1 pois as propriedades da heap são observadas apenas nos níveis mais baixos
Construção de Heap
! Por isso o arranjar()
arranjar (n, H){ para i = n/2, ..., 1 faça descer(i,n, H); }
Complexidade da Construção de Heap
! Suponhamos que a árvore seja cheia, sem perda de generalidade. Então,
● n = 2h – 1, onde h é a altura
" desses, apenas 2h–1 – 1 são nós internos
● A raiz (1 nó) da árvore pode descer no máximo h – 1 níveis
● Os dois (21) nós de nível 2 podem descer h – 2 níveis
● Os quatro (22) nós do nível 3 podem descer h – 3 níveis
● .....
● Os 2h – 2 nós de nível h – 1 podem descer 1 nível
● Então, cada nível j tem no máximo piso(n/2j) nós
1
2
3
4
Complexidade da Construção de Heap
! Logo, o número de passos totais é o somatório de :
● j n/2j
● que é O(n)
1
2
3
4
27
Máximos e Mínimos
# Lista de prioridades # acesso constante ao elemento de maior prioridade
# Em uma generalização # permitir acesso constante ao de menor prioridade
heap min-max # lista linear S de chaves si
# nivel(i) = log i +1 # nó i está em
# nível máximo se for par, # senão, em nível mínimo
28
Máximos e Mínimos
S é um heap min-max se s1 é a menor chave e para todo i # i está em um nível minimo
# si <= si/2
# si >= si/4
# i está em um nível máximo # si >= si/2
# si <= si/4
29
Máximos e Mínimos
S é um heap min-max se s1 é a menor chave e para todo i # i está em um nível mínimo
# si <= si/2
# si >= si/4
# i está em um nível máximo # si >= si/2
# si <= si/4
20
70
8
4
35
38
19
30 28 18 25 36 37 26 22
s1
s2 s3
s4 s5 s6 s7
s8 s9
mínimo
máximo
mínimo
máximo
30
Máximos e Mínimos
• s1 é a menor elemento • o maior elemento é o maior entre os filhos da raiz
20
70
8
4
35
38
19
30 28 18 25 36 37 26 22
s1
s2 s3
s4 s5 s6 s7
s8 s9
mínimo
máximo
mínimo
máximo
top related