B-Árvores

Autor: Luiz Filipe de Almeida

Dado um inteiro m >= 2, uma B-árvore de ordem m é um árvore multiária de busca que satisfaz as seguintes condições:

Fato: Para uma B-árvore de ordem t com n >= 1 elementos e altura h vale:

h <= log (n+1/2)

onde o log é na base t. Assim, dado que as operações levam tempo proporcional à altura da árvore, quanto maior a ordem da árvore, mais elementos poderão ser alojados nela sem prejuízo da velocidade de busca/inserção/remoção.

Representação de uma célula

A célula genérica de uma B-árvore pode ser descrita em C através de uma struct do tipo:

Struct B_celula
{
int nchaves;
struct B_celula filho[2*ORDEM];
int chaves[2*ORDEM-1], folha;
}

onde:

 

Operações em B-árvores

Listagem

Para percorrer e imprimir todos os elementos de uma B-árvore, basta uma função simples como esta:


void percorreB (struct celula_B* r)
// recebe um apontador para uma B-árvore e imprime a chave de todas as suas células
{
  int i;
  if (r != NULL)
  {
    for (i = 0; i < r->nchaves; i++)
    {
      percorreB (r->filho[i]);
      printf ("%d", r->chave[i]);
    }
    percorreB (r->filho[r->nchaves+1]);
  }
}

Busca


struct B_celula*
buscaB ( struct B_celula*r, int x)
// devolve um apontador para a célula da árvore que contém a chave x
{
  if (r == NULL) return NULL;
  for (i = 0; i < r->nchaves && r->chave[i] < x, i++);
  if (i == r->nchaves) return buscaB (r->filho[i], x);
  if (r->chave[i] == x) return r;
  return buscaB(r->filho[i], x);
}

Inserção

Inserir um elemento em uma B-árvore é um pouco mais complicado que inserir um elemento em uma árvore binária de busca. Uma operação fundamental usada durante a inserção é a quebra de um nó cheio ao redor de seu elemento mediano (vide figura a seguir).

Para fazer essa quebra, é utilizada uma função como esta:


struct celula_B*
quebra_celula (struct celula_B* r, struct celula_B* f, int i )
// recebe um apontador f para o elemento a quebrar e um apontador r para seu pai, e
// devolve um apontador u para  a segunda "metade" após a quebra ("metade" que acaba de ser criada)
{
  struct celula_B* u;
  u = (struct celula_B*) malloc (sizeof (struct celula_B));
  u->folha = f-> folha;  // TRUE se f é folha
  u->nchaves = f->nchaves = ORDEM-1;    // As duas partes terão no máximo a metade de sua capacidade utilizada

  for (j = 0; j < ORDEM; j++)
    u->filho[j] = f->filho[ORDEM+j];    // copia a "segunda metade" dos elementos 
                                        // de f para a "primeira metade" dos 
                                        // elementos de u

  for ( j = r->nchaves+1; j > i; j--)
    r->chave[j] = r->chave[j-1];        // Abre espaço em r para o novo elemento
  r->chave[i] = f->chave[ORDEM-1];      // O elemento "do meio" de f  "sobe"
                                        // para a vaga criada na célula r
  r->nchaves++;
 
 for (j = r->nchaves+1; j > i + 1; j--)
    r->filho[j] = r->filho[j-1];        // Acerta todos os ponteiros para os filhos de r

  r->filho[i + 1] = u;
  return u;
}


Last modified: Thu Jun 14 20:05:38 BRST 2001