Última aula de Estrutura de dados
Mac2301 - primeiro semestre de 2002
Nestes BDs, é necessária uma forma bem eficiente de representar os dados. Um forma seria o uso de árvores de busca binária, ou árvores AVL. Entretanto estas estruturas podem ficar desbalanceadas, e mesmo em condições favoráveis a diferença de altura entre folhas pode ser considerável. Estas diferenças são sentidas principalmente quando as árvores são muito grandes e não podem ser armazenadas inteiramente na mémoria, e devem ser guardadas no disco.
Para garantir que a estrutura esteja sempre perfeitamente balanceada (todas as folhas estão no mesmo nível, ou profundidade), novas estruturas de dados serão necessárias.
A árvore 2-3 não é binária, mas obedece às seguintes propriedades:
Além disto, a árvore 2-3 tem as mesmas propriedades que uma árvore binária de busca. Para cada nó, o valor de todos os decendentes na árvore a esquerda são menores que a primeira chave, os valores na árvore do centro são maiores ou iguais a primeira chave. Se existe uma terceira sub-árvore, as chaves da árvore central são menores do que o valor da segunda chave, e as chaves armazenadas na árvore da direita são maiores ou iguais a segunda chave. Veja um exemplo abaixo, na figura 1:
O código para a estrutura segue abaixo:
typedef struct _no23 { int lkey, // chave esquerda rkey, // chave direita nkeys; // número de chaves struct _no23 *left, // ponteiro ao filho esquerdo *center, // ponteiro ao filho central *right; // ponteiro ao filho direito } no23;a seguinte função retorna o nó com a chave (se este estiver na árvore):
no23 *find(no23 *raiz, int key) { if (raiz==NULL) return NULL; // não encontrou if (key == raiz->lkey) return raiz; // é a chave esquerda if ((raiz->nkeys == 2) && (key == raiz->rkey)) return raiz; // é a chave direita if (key < raiz->lkey) return find(raiz->left, key); else if (raiz->nkeys == 1) return find(raiz->center, key); else if (key < raiz->rkey) return find(raiz->center, key); else return find(raiz->right, key); }
A inserção em uma árvore 2-3 é parecida com a inserção em uma árvore binária de busca, o novo registro é armazenado em um nó folha. O mecanismo é o seguinte: primeiro deve se encontrar o nó folha onde deveria estar a chave a ser enserida. Se este nó contém apenas um valor, basta adicionar o novo valor a este nó. Veja um exemplo disto na figura seguinte.
![]() |
Se a chave deve ser inserida em um nó que já contém duas chaves,
mais espaço deve ser criado. Considere os dois valores já existentes, e
a nova chave. Primeiro o nó
é quebrado em dois nós,
(antigo
) e
. A menor chave fica em
, e a maior fica em
. Em
seguida o valor intermediário é passado para o nó pai de
acompanhado
de um ponteiro para
. Este mecanismo é denominado promoção. A chave
promovida é então inserida no nó pai. Caso este tenha apenas duas
chaves, esta chave e um ponteiro para
são adicionados. Caso
contrário, se o pai já continha três chaves, o processo de quebra e
promoção é repetido. Veja na figura 3 como fica a árvore
inicial após a inserção da chave 55.
![]() |
A próxima figura mostra a árvore da figura 1 após a inserção da chave 19, que vai provocar a quebra, e promoção da raiz.
![]() |
Segue abaixo o pseudo-código da inserção em uma árvore 2-3. Neste trecho de são apresentadas apenas as funções principais. Além delas são utilizados os seguintes protótipos:
// cria um nó com ch1, ch2 e nchaves, assim como os três ponteiros no23 *criaNo(int ch1, int ch2, int nchaves, no23 *pl, no23 *pc, no23 *pr); // verifica se o nó em questão é folha, volta 1 se sim, e 0 caso contrario int isLeaf(no23 *no); // coloca uma nova chave ch e arvore p, em um nó com apenas uma chave void adicionaChave(no23 *no, int ch, no23 *p); // Quebra um nó em dois, sendo val e subarvore, os novos dados no23 *quebraNo(no23 *no, int val, int *rval, no23 *subarvore);
Função principal insere:
// insere val em no (se necessario retorna o novo no e um valor // rval) no23 *insere( no23 **no, int val, int *rval){ no23 *paux, *paux2; int vaux, promov; if (*no == NULL) { // arvore vazia *no = (no23 *) malloc (sizeof(no23)); *no = criaNo(val, 0, 0, NULL, NULL, NULL); // cria no folha com um valor return NULL; // nada a fazer depois } if (isLeaf(*no)){ // chegou a folha if ((*no)->nkeys == 1){ // caso fácil adicionaChave(*no, val, NULL); return NULL; } else { paux = quebraNo(*no, val, &vaux, NULL); *rval = vaux; return paux; } } else { // continua a procura if (val < (*no)->lkey) paux = insere( &((*no)->left), val, &vaux); else if (((*no)->nkeys == 1) || (val < (*no)->rkey)) paux = insere( &((*no)->center), val, &vaux); else paux = insere( &((*no)->right), val, &vaux); if (paux == NULL) // nao promoveu return NULL; else if ((*no)->nkeys == 1){ adicionaChave(*no, vaux, paux); return NULL; } else { paux2 = quebraNo(*no, vaux, &promov, paux); *rval = promov; return paux2; } } }A única função não óbvia é a quebraNo, dada abaixo:
no23 *quebraNo(no23 *no, int val, int *rval, no23 *subarvore){ no23 *paux; if (val > no->rkey) { // val esta mais a direita *rval = no->rkey; // promove a antiga maior paux = no->right; no->right = NULL; // elimina o terceiro filho no->nkeys = 1; // atualiza o número de chaves return criaNo(val, 0, 1, paux, subarvore, NULL); } else if (val >= no->lkey){ // val esta no meio *rval = val; // continua sendo promovido paux = no->right; no->right = NULL; no->nkeys = 1; return criaNo(no->rkey, 0, 1, subarvore, paux, NULL); } else { // val esta a mais a esquerda *rval = no->lkey; // primeiro cria o nó a direita paux = criaNo(no->rkey, 0, 1, no->center, no->right, NULL); no->lkey = val; // em seguida arruma o nó a esquerda no->nkeys = 1; no->right = NULL; no->center = subarvore; return paux; } }
A remoção é relativamente complexa, excluindo-se os casos simples, de remoção em um nó folha com duas chaves, outras remoções podem levar a casos onde dois nós devem se tornar um único nó.
Para mais informações sugiro os livros da bibliografia, ou o texto
disponível em:
http://www.ime.usp.br/~coelho/mac324/conteudo/B-arvore.html
This document was generated using the LaTeX2HTML translator Version 99.2beta6 (1.42)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split 0 b-arvore
The translation was initiated by Alfredo Goldman on 2002-06-10