Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
ÁRVORES B
Até agora manipulamos a estrutura árvore binária em memória principal. Também
queremos no entanto guardar árvores em disco e carregar a informação do disco para a
memória principal, quando precisamos dela. Como sabemos, a informação em memória
principal é temporária enquanto no disco é permanente, e a memória principal disponível
é muito menor do que espaço em disco, daí a vantagem de podermos ter gravada a
informação em memória secundária. Contudo, o acesso ao disco é muito mais lento do
que o acesso à memória principal e se usássemos a informação em disco, estruturada
numa árvore binária de pesquisa, dado que temos tantos nós quantas as chaves o
número de operações de I/O seria elevado e portanto o tempo exigido, por exemplo,
numa pesquisa seria longo. Para evitar este inconveniente usaremos grandes blocos de
informação agrupando vários objectos, cada um incluindo uma chave, num só nodo. A
escolha da ordem da árvore (do número de elementos por nó) depende do tamanho do
bloco de disco, isto é cada nó deverá ter o tamanho dum bloco de disco para que cada
vez que fazemos um acesso a um nó para uma operação de pesquisa ou actualização só
precisemos de realizar uma única transferência do disco.
Usaremos então uma árvore n-ária de determinado tipo designada por árvore B.
Neste tipo de árvores para cada valor M, a que se chama ordem da árvore, cada nodo
contem no máximo 2*M elementos e no mínimo M, excepto o nó raiz, que pode
transgredir esta última regra, admite-se que o nó raiz tenha no mínimo 1 elemento . Em
aplicações reais usamos para M valores da ordem da centena, usar-se-ão sempre chaves
únicas, não existem portanto chaves repetidas.
Nos exemplos que vamos usar para explicar os algoritmos de manipulação deste tipo de
árvores consideraremos cada elemento reduzido a uma chave.
Tal como acontece com as árvores binárias de pesquisa usaremos as chaves para
procurar um elemento na árvore de uma maneira eficiente.
A estrutura de cada nodo de uma árvore B, será dada por um array de chaves (k[i]
elementos ) de comprimento 2*M, um array de apontadores de comprimento 2*M+1 (ptr[i])
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
1
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
e um campo inteiro, cnt, que indica quantos dos 2*M campos de informação estão
realmente ocupados.
Por outras palavras diremos que, cada nodo tem no máximo 2*M+1 filhos , uma vez que
possui 2*M+1 apontadores e no mínimo M. O número de elementos guardados em cada
nodo é menor do que o número de apontadores.
Utilizando a notação simplificada de n, ki e pi em vez de rz->cnt, rz->k[i] e rz->ptr[i] em
que rz é o apontador para o nodo, teremos as seguintes condições a verificar nos nodos
da árvore:
1 <= n <= 2*M para o nó raiz
M<= n <= 2*M para os outros nodos.
Os elementos em cada nodo estão ordenados por chave,
k0 < k1 < k2<...<kn-1
n
p0
k0
p1
k1
p2
k2
p3
...
... ... ...
kn-1
pn
Esquematicamente representa-se abaixo uma árvore B de ordem 2.
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
2
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Se o nodo é uma folha o valor de cada apontador pi é nulo.
Se o nodo não é folha, cada um dos n+1 ponteiros aponta para outro nodo, filho do nodo
dado.
Se i>0 todas as chaves do nó apontado por pi, são maiores do que Ki-1.
Se i<n todas as chaves do nó apontado por pi são menores do que ki.
Resultante do modo de construção ,as árvores B apresentam outra caracterìstica, são
equilibradas e mantêm os nós folha todos ao mesmo nível, logo o caminho do nó raiz a
qualquer folha tem exactamente o mesmo comprimento. Esta forma interessante das
árvores B tem no entanto o seu preço, há muitas vezes espaço de memória que está
reservado e não está a ser utilizado. É sem dúvida uma estrutura elegante e eficiente
para muitas aplicações embora os algoritmos de manipulação não sejam imediatos.
As árvores B foram inventadas por Bayer e McCreight.
Do exposto deduzimos que para o mesmo número de elementos armazenado numa
árvore binária e numa árvore B, a altura da árvore B é muito menor que a da binária.
Para uma árvore B de ordem M, com n elementos e todos os nós completamente
preenchido, a altura será da ordem de O (log 2M+1 n).
Vejamos seguidamente a forma de lhe juntar elementos.
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
3
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Suponhamos que a nossa árvore B estava vazia e vamos juntar-lhe elementos cujas
chaves são os valores: 60, 20, 80, 10. Obteremos então um nó raiz que contem a
seguinte informação:
n=4
p0= p1= p2= p3= p4= Nulo
k0= 10
k1= 20
k2= 60
k3=80
O juntar de um elemento será sempre feito num nó folha
A configuração da árvore será então a seguinte:
Se agora juntássemos o valor 15,
fazendo uma inserção ordenada obteríamos a seguinte sequência:
10,15,20,60,80
Mas como se ultrapassou a capacidade do nó, há que dividir o nó em 2, cada um com M
elementos, neste nosso exemplo cada nó teria dois elementos, e o valor do meio será
inserido no nível superior (neste exemplo, o valor 15), ficando a árvore com a seguinte
configuração:
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
4
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Juntando os valores30 e 70 obteríamos:
Juntemos agora o valor 22,
então a folha mais à direita terá que dividir-se, obtendo-se a seguinte +arvore:
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
5
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Se fossem adicionados os valores: 12, 18, 19, 4, 5, 6, 2, 3 a árvore apresentaria o
seguinte aspecto
A adição do elemento com valor 1 ,vai obrigar a nova configuração da árvore, uma vez
que o nó mais à esquerda será obrigado a dividir-se
o elemento do nó tentará ser
colocado no nó pai mas como este já está totalmente preenchido será também obrigada a
dividir-se subindo o valor do meio para um novo nó raiz, ficando a árvore com a seguinte
representação
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
6
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Uma vez que os valores por nó estão ordenados, a pesquisa em cada nó é feita através
do algoritmo de pesquisa binária, conforme se exemplifica no algoritmo abaixo.
Pesquisa Binária nos nós da árvore B
Os valores de retorno da pesquisa binária serão:
0 se x <= a[0]
n se x > a[n-1]
r se a[r-1] < x <= a[r]
Algoritmo
pesquisa_binaria(x, a, n) // a é o array de chaves, x o valor a procurar, n o numero de
// elementos no nó
Inicio
Se x <= a[0]
Então devolve 0
Fse
Se x > a[n-1]
Então devolve n
Fse
esq = 0
dir = n-1
Enquanto (dir-esq) > 1
i = (dir+esq)/2
Se x <= a[i]
Entao dir = i
Senão esq = i
Fse
Fenquanto
devolve dir
Fim
Descrevemos agora o algoritmo de pesquisa de um valor x, na árvore B de raiz t.
Algoritmo
pesquisar(x,t)
Inicio
Enquanto t!= Nulo
k=t->chave
n=t->cnt
i=pesquisa_binária(x, k, n)
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
7
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Se (i<n e x==k[i])
Então encontrou(t,i)
sai
Fse
t=t->ptr[i]
Fenquanto
escreve "Item" x "não encontrado"
Fim
Algoritmo
encontrou (t, i)
Inicio
escreve "Encontrado na posição " i
escreve "do nó com os seguintes conteúdos : "
Para i=0 até i < t->cnt
escreve t->chave[i]
i++
Fpara
Fim
Quanto ao algoritmo de inserção de elementos numa árvore B, atendendo ao que já foi
dito, recorreremos a um algoritmo recursivo, ins, que descerá na árvore desde a raiz até
à folha onde será junto o elemento x, e no caminho de regresso da recursividade será
colocado o elemento que sobe quando se dá a divisão de nós, designado por xnovo, e
será ligado o novo nó, designado por tnovo.
O valor de retorno deste algoritmo recursivo será 0, 1 ou 2 indicando ao pai
respectivamente se a operação ainda não está completa (caso de divisão de nó no nível
abaixo), se a operação está terminada ou se houve duplicação de chave.
A invocação do método de inserir poderá ser feito do seguinte modo:
raiz =inserir( x, raiz)
Algoritmo:
inserir(x,t)
Inicio
codigo=ins(x, t, xnovo, tnovo) // xnovo e tnovo são passados por referência
Se (codigo==2)
Então "Chave duplicada"
Se (codigo==1)
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
8
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Então devolve t
Senão u=obternodo()
u->cnt=1
u->chave[0]=xnovo
u->ptr[0]=t
u->ptr[1]=tnovo
devolve u
Fim
codigo==0 Se o novo elemento ainda não foi junto à árvore
codigo==1 Se o novo elemento já se juntou à árvore
codigo==2 Se o elemento já existe na árvore
Algoritmo recursivo
ins(x, t, y, u)
// y e u por referencia
Inicio
//Consideramos:
p=t->ptr
n=t->cnt
k=t->chave
//Verificar se t é um apontador numa folha
Se (t==Nulo)
Então u=Nulo
y=x
devolve 0
//Seleccionar o apontador p[i] e tentar inserir x na subárvore em que p[i] é raiz
i=pesquisa_binária(x, k, n) // array de chaves do nó
Se (i<n e x==k[i])
Então devolve 2 // chave duplicada
Fse
codigo=ins(x, p[i], xnovo, tnovo)
Se (codigo)
Então devolve codigo
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
9
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Fse
//A inserção na subárvore não foi completamente sucedida
//Vamos tentar inserir xnovo e tnovo no nó corrente
Se (n<MM)
Então i=pesquisa_binária(xnovo, k, n)
Para j=n até j>i
k[j]=k[j-1]
p[j+1]=p[j]
j -Fpara
k[i]=xnovo
p[i+1]=tnovo
n++
devolve 1
Fse
/*O nó corrente está cheio, por isso há que dividir. Passar o item k[M], no meio da
sequência aumentada para trás, através do parâmetro y, para que possa subir na árvore.
Também passa um apontador para o nó criado de novo para trás, através de u. Devolve 0
para indicar que a inserção não está completa.
*/
Se (i==MM)
Então k_final=xnovo
p_final=tnovo
Senão k-final=k[MM-1]
p_final=p[MM]
Para j=MM-1 até j>i
k[j]=k[j-1]
p[j+1]=p[j]
j -Fpara
k[i]=xnovo
p[i+1]=tnovo
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
10
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
Fse
y=k[M]
n=M
u=obternodo()
u->cnt=M
Para j=0 até j<M-1
u->k[j]=k[j+M+1]
u->ptr[j]=p[j+M+1]
j ++
Fpara
u->ptr[M-1]=p[MM]
u->k[M-1]=k_final
u->ptr[M]=p_final
devolve 0
FIM
Quanto à operação de eliminação de um elemento, proceder-se-á de forma inversa da de
inserção.
Assim, o primeiro passo consiste na pesquisa do elemento a eliminar. Se o nó onde se
encontra o elemento é um nó folha então há que retirar o elemento do array. Se o
resultado dessa eliminação é um nó com menos de M elementos, é realizada uma
reorganização como se segue:
Se existe dentro da mesma subárvore, um irmão direito com mais de M elementos, à
folha em questão pode ser junto um elemento emprestado da folha irmã, envolvendo o nó
pai, isto é, o elemento mais à esquerda da irmã direita sobe para o pai e o do pai desce
para a folha onde foi efectuada a eliminação. Alternativamente, caso a folha irmã não
possa emprestar nenhum elemento porque só tem M elementos, haverá que juntar as
duas folhas. Neste caso, também um elemento do pai descerá para a folha nova, ficando
esta com um máximo de 2*M elementos. Se por acaso o pai ficar com menos de M
elementos esta mesma reorganização terá que se repetir nos níveis superiores.
No caso do elemento a eliminar não pertencer a uma folha, mas a um nó interno, então a
posição do elemento que vai sair é substituído pelo valor do elemento imediatamente
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
11
Departamento de Engª Informática
Estruturas de Informação
Árvores B
_______________________________________________________________________________________________
anterior em ordem simétrica, isto é , do filho esquerdo o mais à direita, até atingir uma
folha( método idêntico ao usado nas árvores binárias de pesquisa). Se por acaso a folha
contem M elementos será então necessário proceder à reorganização da árvore como
explicado no caso da eliminação de um elemento pertencente a um nó folha.
_______________________________________________________________________________
Instituto Superior de Engenharia do Porto
12
Departamento de Engª Informática
Download

Árvores B - Departamento de Engenharia Informática