Você está na página 1de 12

s3

s2 s4

s1

Figure 1: T1 com n = 4 chaves

PROJETO E ANÁLISE DE ALGORITMOS


Comentários e tarefas para aula suspensa do dia 26/03/2020

ÁRVORE BINÁRIA DE BUSCA DE CUSTO ÓTIMO:

Seja S um conjunto de chaves S = {s1 , s2 , ..., sn }, s1 < s2 < . . . < sn e freqüências de acesso
f0 , f1 , . . . fn .
As chaves são alocadas nos nós de uma árvore binária de busca T .
Para cada chave sk (com freqüência de acesso fk ), seja lk o nı́vel que corresponde a sk em T .
O conjunto de números reais pode ser dividido em:

(−∞, s1 ), s1 , (s1 , s2 ), s2 , ....., sn−1 , (sn−1 , sn ), sn , (sn , ∞)


Notamos por:
R0 = (−∞, s1 ),
Ri = (si , si+1 ), 1 ≤ i ≤ n − 1,
Rn = (sn , ∞).

Por exemplo, para n = 4, considere as chaves s1 < s2 < s3 < s4 .

A árvore T1 da Figura 1 mostra uma árvore binária de busca para essas 4 chaves.

1
s3

s2 s4

s1 R2 R3 R4

R0 R1

Figure 2: T1 com nós externos representando os possı́veis casos de busca sem sucesso

Se procurarmos por um valor x qualquer na árvore T1 , podem acontecer 2 situações: x será


igual a uma das 4 chaves alocadas na árvore (busca com sucesso), ou x não será igual a nenhuma
delas (busca sem sucesso). Todas as buscas com sucesso, correspondem ao caso de x ser igual a
um dos 4 valores si . Todos as buscas sem sucesso correspondem ao valor de x pertencer a algum
dos conjuntos R0 , R1 , R2 , R3 , R4 . Podemos representar as buscas sem sucesso com nós, denomi-
nados nós externos, representados por nós retangulares na árvore T1 como mostra a Figura 2.

Se contamos o número de comparações totais realizadas para cada uma das buscas com
sucesso podemos considerar o nı́vel do nó que armazena cada chave e a freqüência associada à
mesma, denominado comprimento do caminho ponderado interno a:
4
X
fp lp .
p=1

Consideremos para cada um dos nós externos freqüências associadas f00 , f10 , f20 , f30 , f40 . Para
cada uma das buscas sem sucesso, podemos também calcular todas as comparações realizadas
utilizando lp0 − 1 (a última comparação da busca sem sucesso no nó que é o pai do nó externo
Rp que representa a situação da chave x) e a frequência correspondente ao nó externo Rk ,
denominado comprimento do caminho ponderado externo a:
4
fp0 (lp0 − 1).
X

p=0

O custo associado a uma árvore binária de busca T para n chaves com freqüências de acesso:
f1 , . . . , fn associadas às chaves e f00 , . . . , fn0 associados àos nós externos é defnido pela soma do
comprimento do caminho interno ponderado mais o comprimento do caminho externo externo
ponderado, ou :

n n
fp0 (lp0 − 1).
X X
C(T ) = fp lp +
p=1 p=0

2
s2

s1 s3

R0 R1 R2 s4

R3 R4

Figure 3: T3

Para a árvore binária de busca T1 da Figura 2 para as 4 chaves se considerarmos as freqüências


f1 = 10, f2 = 1, f3 = 3, f4 = 2 e f00 = 2, f10 = 1, f20 = 1, f30 = 1, f40 = 1 o custo seria:

C(T2 ) = (10.3 + 1.2 + 3.1 + 2.2) + (2.4 + 1.4 + 1.3 + 1.3 + 1.3) = 39 + 21 = 60.

Pergunta 1: poderiamos alocar as 4 chaves em posições diferentes em outra árvore binária


de busca para obter custo menor?

Para o mesmo conjunto de freqüências fp e fp0 , a árvore binária de busca T3 da Figura 3 tem
custo:

C(T3 ) = (10.2 + 1.1 + 3.2 + 2.3) + (2.3 + 1.3 + 1.3 + 1.4 + 1.4) = 33 + 20 = 53.

Observar que 53 = C(T3 ) < C(T2 ) = 60.

Pergunta 2: podemos obter uma outra árvore binária de busca para as 4 chaves com as
freqüências fp e fp0 dadas que apresente menor custo?

A resposta para a Pergunta 2 é dada pelo Algoritmo 4.3.

O Algoritmo 4.3 se baseia nos seguintes fatos:

Lema 1:
As subárvores de uma árvore binária de busca ótima são também ótimas.

Seja T (i, j) a árvore ótima corrspondente ao subconjunto de chaves


{si+1 , . . . , sj }, 0 ≤ i ≤ j ≤ n.
Seja F (i, j) a soma de todas as freqüências correspondentes a T (i, j), ou seja
F (i, j) = i<p≤j fp + i≤p≤j fp0 .
P P

3
Lema 2:
Seja T (i, j) a árvore binária de busca ótima de raiz sk correspondente as chaves {si+1 , . . . , sj },
0 ≤ i ≤ j ≤ n. Então C(T (i, j)) = C(T (i, k − 1)) + C(T (k, j)) + F (i, j).

ALGORITMO 4.3:
0) Considere C matriz de dimensão (n + 1) × (n + 1) (as linhas e colunas numeradas de 0 até n).
1) Calcular C(T (0, n)) usando o Lema 1 recursivamente.
2) Os valores iniciais são C(T (i, i)) = 0, 0 ≤ i ≤ n (elementos da diagonal principal da matriz C
3) Devido a que o valor C(T (i, j)) será utilizado varias vezes durante o algoritmo, armazenar
estes valores numa tabela para poder dispor deles cada vez que seja necessário (número total de
custos = número total de pares (i, j), 0 ≤ i < j ≤ n = n(n+1)2 )
4) Calcular C(T (i, j)) a partir de C(T (i, k − 1)), C(T (k, j)) (devem estar calculados antes de
usa-los e portanto k − 1 − i < j − i e j − k < j − i) e F (i, j)).

O Algoritmo 4.3 preenche as posições da matriz C por diagonal, na parte superior da matriz.
As diagonais da matriz C são representadas por valores d = i−j entre 0 e n. Dado o elemento da
matriz C na posição i, j da parte triangular superior (j ≥ i), se d = j − i = 0, o elemento C(i, j)
está posicionado na diagonal principal da matrix (primeiro laço para do procedimento a seguir),
se d = j − i = 1 o elemento C(i, j) está posicionado na diagonal acima da diagonal principal
(primeira iteração do laço para, considerando d variando de 1 até n), e assim por diante, se
d = j − i = n o elemento C(i, j) está posicionado na primeira linha e última coluna da matriz
C (C(0, n)) (última iteração do laço para, considerando d variando de 1 até n).

ENTRADA:n e f1 , . . . , fn , f00 , . . . , fn0 .

SAÍDA: C(0, n): elemento da matriz C na linha i = 0 e coluna j = n.

ALGORITMO 4.3 (ARVORE BINARIA OTIMA 1)(n, f, f 0 , C)


para j = 0, . . . , n faça
C[j, j] = 0, F [j, j] = fj0
para d = 1, . . . , n faça
para i = 0, . . . , n − d faça
j =i+d
F [i, j] = F [i, j − 1] + fj + fj0
C[i, j] = mini<k≤j {C[i, k − 1] + C[k, j]} + F [i, j]

A complexidade do Algoritmo 4.3 é O(n3 ) (por conta do mı́nimo calculado dentro de dois
laços para aninhados).

4
d=0

R0 R1 R2 R3 R4

Figure 4: Os custos das 5 árvores contendo zero chaves e um único nó externo são os
elementos da diagonal na matriz C (d = 0)

Exemplo:
Sejam n = 4,
f1 = 10, f2 = 1, f3 = 3, f4 = 2 e
f00 = 2, f10 = 1, f20 = 1, f30 = 1, f40 = 1.
C e F são matrizes de dimensão 5 × 5 = (n + 1) × (n + 1).
A inicialização das matrizes C e F (diagonal principal das duas matrizes C(j, j) e F (j, j),
0 ≤ j ≤ 4 é mostrada na Figura 4:
   
0 2

 0 


 1 

C= 0 F = 1
   

   
 0   1 
0 1

Para d = 1 (d = j − i) Ver árvores cujos custos são calculados neste caso na Figura 5.

i = 0, j = 1
F [0, 1] = F [0, 0] + f1 + f10 = 2 + 10 + 1 = 13
C[0, 1] = min0<k≤1 {C[0, k − 1] + C[k, 1]} + F [0, 1] = min{C[0, 0] + C[1, 1]} + 13 = 0 + 13 = 13

i = 1, j = 2
F [1, 2] = F [1, 1] + f2 + f20 = 1 + 1 + 1 = 3
C[1, 2] = min1<k≤2 {C[1, k − 1] + C[k, 2]} + F [1, 2] = min{C[1, 1] + C[2, 2]} + 3 = 0 + 3 = 3

i = 2, j = 3
F [2, 3] = F [2, 2] + f3 + f30 = 1 + 3 + 1 = 5
C[2, 3] = min2<k≤3 {C[2, k − 1] + C[k, 3]} + F [2, 3] = min{C[2, 2] + C[3, 3]} + 5 = 0 + 5 = 5

i = 3, j = 4
F [3, 4] = F [3, 3] + f4 + f40 = 1 + 2 + 1 = 5
C[3, 4] = min3<k≤4 {C[3, k − 1] + C[k, 4]} + F [3, 4] = min{C[3, 3] + C[4, 4]} + 4 = 0 + 4 = 4
   
0 13 2 13

 0 3 


 1 3 

C= 0 5 F = 1 5
   
 
   
 0 4   1 4 
0 1

5
d=1
s1 s2 s2 s2

R0 R1 R1 R2 R2 R3 R3 R4

Figure 5: Os custos das 4 árvores ótimas contendo uma única chave e dois nós externos
são os elementos da segunda diagonal na matriz C (d = 1)
d=2
s1 s3 s3

R0 s2 s2 R3 R2 s4

R1 R2 R1 R2 R3 R4

Figure 6: Os custos das 3 árvores ótimas contendo duas chaves consecutivas e três nós
externos são os elementos da terceira diagonal na matriz C(d = 2)

Para d = 2, ver árvores cujos custos são calculados neste caso na Figura 6.

i = 0, j = 2
F [0, 2] = F [0, 1] + f2 + f20 = 13 + 1 + 1 = 15
C[0, 2] = min0<k≤2 {C[0, k −1]+C[k, 2]}+F [0, 2] = min{C[0, 0]+C[1, 2]; C[0, 1]+C[2, 2]}+15 =
min{0 + 3; 13 + 0} + 15 = 3 + 15 = 18

i = 1, j = 3
F [1, 3] = F [1, 2] + f3 + f30 = 3 + 3 + 1 = 7
C[1, 3] = min1<k≤3 {C[1, k − 1] + C[k, 3]} + F [1, 3] = min{C[1, 1] + C[2, 3]; C[1, 2] + C[3, 3]} + 7 =
min{0 + 5; 3 + 0} + 7 = 3 + 7 = 10

i = 2, j = 4
F [2, 4] = F [2, 3] + f4 + f40 = 5 + 2 + 1 = 8
[2C, 4] = min2<k≤4 {C[2, k − 1] + C[k, 4]} + F [2, 4] = min{C[2, 2] + C[3, 4]; C[2, 3] + C[4, 4]} + 8 =
min{0 + 4; 5 + 0} + 8 = 4 + 8 = 12
   
0 13 18 2 13 15

 0 3 10 


 1 3 7 

C= 0 5 12  F =  1 5 8 
   
   
 0 4   1 4 
0 1

6
d=3
s1 s3

R0 s3 s2 s4

s2 R3 R3 R4 R3 R4

R1 R2

Figure 7: Os custos das 2 árvores ótimas contendo três chaves consecutivas e quatro nós
externos são os elementos da quarta diagonal na matriz C (d = 3)

Para d = 3, ver árvores cujos custos são calculados neste caso na Figura 7.

i = 0, j = 2
F [0, 3] = F [0, 2] + f3 + f30 = 15 + 3 + 1 = 19
C[0, 3] = min0<k≤3 {C[0, k−1]+C[k, 3]}+F [0, 3] = min{C[0, 0]+C[1, 3]; C[0, 1]+C[2, 3]; C[0, 2]+
C[3, 3]} + 19 = min{0 + 10; 13 + 5; 18 + 0} + 19 = 10 + 19 = 29

i = 1, j = 4
F [1, 4] = F [1, 3] + f4 + f40 = 7 + 2 + 1 = 10
C[1, 4] = min1<k≤4 {C[1, k−1]+C[k, 4]}+F [1, 4] = min{C[1, 1]+C[2, 4]; C[1, 2]+C[3, 4]; C[1, 3]+
C[4, 4]} + 10 = min{0 + 12; 3 + 4; 10 + 0} + 10 = 7 + 10 = 17
   
0 13 18 29 2 13 15 19

 0 3 10 17 
 
 1 3 7 10 
C= 0 5 12  F =  1 5 8 
   
   
 0 4   1 4 
0 1

Para d = 4, ver árvores cujos custos são calculados neste caso na Figura 8.

i = 0, j = 4
F [0, 4] = F [0, 3] + f4 + f40 = 19 + 2 + 1 = 22
C[0, 4] = min0<k≤4 {C[0, k−1]+C[k, 4]}+F [0, 4] = min{C[0, 0]+C[1, 1]; C[0, 1]+C[2, 4]; C[0, 2]+
C[3, 4]; C[0, 3] + C[4, 4]} + 22 = min{0 + 17; 13 + 12; 18 + 4; 29 + 0} + 22 = 17 + 22 = 39
   
0 13 18 29 39 2 13 15 19 22

 0 3 10 17 


 1 3 7 10  
C= 0 5 12  F =  1 5 8 
   
   
 0 4   1 4 
0 1

7
d=4
s1

R0 s3

s2 s4

R1 R2 R3 R4

Figure 8: O custo da única árvore ótima contendo quatro chaves consecutivas e cinco nós
externos são os elementos da quinta diagonal na matriz C (d = 4)

8
s1

R0 T (1, 4)

Figure 9:

O algoritmo determina o custo da árvore binária ótima. Os desenhos das árvores dos custos
obtidos durante a execução do algoritmo são dados para ilustrar o que está sendo calculado.
Para construir a árvore binária com custo ótimo deve ser armazenado o valor de k que efetiviza
o mı́nimo no cálculo de C[i, j] na entrada K[i, j] da matriz K. Com uma pequena modificação
do Algoritmo 4.3, o valor de k é armazenado.

ALGORITMOS 4.3.1 (ARVORE BINARIA OTIMA 2)(n, f, f 0 , C, K)


para j = 0, . . . , n faça
C[j, j] = 0, F [j, j] = fj0
para d = 1, . . . , n faça
para i = 0, . . . , n − d faça
j =i+d
F [i, j] = F [i, j − 1] + fj + fj0
C[i, j] = mini<k≤j {C[i, k − 1] + C[k, j]} + F [i, j]
K[i, j] = k

     
0 13 18 29 39 2 13 15 19 22 − 1 1 1 1
 0 3 10 17   1 3 7 10   − − 2 3 3 
     
C= 0 5 12  F =  1 5 8 K= − − − 3 3
     

 − − − −
     
 0 4   1 4  4 
0 1 − − − −

Utilizando a matriz K podemos reconstruir a árvore binária de busca de custo ótimo, T ∗


para as 4 chaves s1 < s2 < s3 < s4 , com freqüências f e f 0 dadas.

Na posição (0, 4) da matriz K obtemos a chave na raiz da árvore T ∗ . Como K(0, 4) = 1,


sabemos que s1 está na raiz. Como T ∗ é uma árvore binária de busca, e s1 é a menor chave, a
subárvore esqueda de s1 não vai conter chaves (só o nó externo R0 ). A subárvore direita de s1
é T (1, 4) pois deve conter as chaves s2 , s3 , s4 (ver Figura 9).

Para saber qual das 3 chaves está na raiz desta subárvore procuramos pelo elemento K(1, 4),
neste caso igual a 3. Portanto s3 é a raiz de T (1, 4). As chaves restantes (s2 e s4 ) são raiz da
subárvore esquerda e direita, pela definição de árvore binária de busca.Para conlcuir, acrescentar
os nós externos correspondentes: T (1, 1) = R1 , T (2, 2) = R2 , T (3, 3) = R3 , T (4, 4) = R4 . As

9
s1

R0 s3

T (1, 2) T (3, 4)

Figure 10:

s1

R0 s3

s2 s4

T (1, 1) T (2, 2) T (3, 3) T (4, 4)

Figure 11:

Figuras 10, 11 e 12 ilustram como terminar a construção da árvore binária de custo ótimo igual
a 39.

10
s1

R0 s3

s2 s4

R1 R2 R3 R4

Figure 12:

LISTA DE PRIORIDADES:

Exercı́cio A:
Considere uma lista T = [95, 60, 78, 39, 28, 98, 70, 33] de tamanho n = 8. Aplicar o procedimento
subir (algoritmo 6.1) para as entradas:
i) i = 6,
ii) i = 3,
iii) i = 1.

algoritmo 6.1
procedimento subir(i)
j = b 2i c
se j ≥ 1 entao
se T [i].chave > T [j].chave entao
trocar T [i] com T [j]
subir(j)

Exercı́cio B:
Considere uma lista T = [37, 60, 78, 39, 28, 66, 70, 33] de tamanho n = 8. Aplicar o procedimento
descer (algoritmo 6.2) para as entradas:
i) i = 1 n = 8,
ii) i = 3 n = 8,
iii) i = 7 n = 8.

algoritmo 6.2
procedimento descer(i, n)
j = i.2
se j ≤ n entao

11
se j < n
se T [j + 1].chave > T [j].chave entao j = j + 1
se T [i].chave < T [j].chave entao trocar T [i] com T [j]
descer(j, n)

Exercı́cio C:
Seja T uma lista de prioridades de tamanho n = 8, T = [95, 60, 78, 39, 28, 66, 70, 33]. Considere
M = 100 o espaçøde memória disponı́vel. Inserir chave x = 73 usando o algoritmo 6.3.

algoritmo 6.3 (Inserção de chave x em lista de prioridades T de tamanho n)


procedimento (T, n, x)
se n < M entao
T [n + 1].chave = x
n=n+1
subir(n)
senão ”overflow”

Exercı́cio D: Seja T uma lista de prioridades de tamanho n = 9, T = [95, 73, 78, 60, 28, 66, 70, 33, 39].
Remover o primeiro elemento da estrutura usando o algoritmo 6.4.

algoritmo 6.4 (Remoção do primeiro elemento na lista de prioridades T de tamanho n)


procedimento (T, n)
se n 6= 0 entao
agir(T [1])
T [1] = T [n]
n=n−1
descer(1, n)
senão ”overflow”

Fazer os Exercı́cios 7 a 14 da Lista 4.

12

Você também pode gostar