Implementação em C de uma B-Tree de propósito geral.
DC - UFSCar - São Carlos - 2015
- Antonio Carlos Falcão Petri
- Thiago Yonamine
Graduandos em Ciência da Computação na UFSCar - São Carlos.
Esse projeto é um trabalho da disciplina Organização e Recuperação da Informação ministrada pelo Prof. Dr. Ricardo Cerri no 2º semestre de 2015 no Departamento de Computação, UFSCar - São Carlos.
Caso você possua a ferramenta GNU Make disponível, basta executar:
$ make
$ ./bin/btreeCompile a aplicação e o test/debug.c com o define: -DDEBUG=1.
Com o makefile, basta descomentar o conteúdo da variável CFLAGS no cabeçalho:
CFLAGS := -g -Wall -DDEBUG=1; e executar:
$ make clean
$ make debug
$ ./bin/debug- Um mesmo programa pode possuir várias B-Tree de ordens diferentes;
- A B-Tree pode armazenar qualquer tipo de dado, desde que:
- ele seja indexável por um valor inteiro;
- ele seja gerenciado pela aplicação externa (a B-Tree não gerenciará o ciclo de vida dos dados).
- As funções
btree_insert(),btree_find()ebtree_remove()retornam umnode_position, conforme determinado na especificação do trabalho. Esteja ciente desse comportamento. O endereço indicado pelonode_positionpode se tornar desatualizado facilmente e, no caso da remoção, pode apontar para um endereço que foi liberado e possui lixo de memória.
Essa implementação foi fortemente baseada nas notas do livro Introduction to Algorithms, Third Edition - Cormen, T.; Leiserdon, C. E.; Rivest, R. L.; Stein, C. n e nas notas de aula do Prof. Ricardo Cerri sobre Árvores B.
Os Pseudo-códigos descritos aqui são modificações dos que se encontram no Introduction to Algorithms.
As implementações das operações Inserção e Busca foram, basicamente, transcrições entre os pseudo-códigos do livro para a linguagem C.
Já a operação Remoção não possuia pseudo-código correspondente, e foi implementada realizando uma análise dos casos especiais descritos no livro.
pair_t- Associação entre
keyevalue:pair<int key, void *value>
- Associação entre
node_t- Nó da B-Tree. Pode possuir [1, 2*order)
chavese [1, 2*order]nós-filhos. Possui onúmero de chaves ativase a flagis_leaf
- Nó da B-Tree. Pode possuir [1, 2*order)
node_position_t->typedef node_position- Permite acessar diretamente um
nodee, em especial, o valor associado à umakey. Possui para tal oponteiro para um nóe oindexde uma determinada chave. É o resultado de várias operações na B-Tree
- Permite acessar diretamente um
btree_t->typedef BTree- Struct principal. Possui apenas o
nó raiz, e aordemda árvore
- Struct principal. Possui apenas o
-
BTree* btree_new(int order);- Aloca e retorna uma nova B-Tree (Heap).
- Deve ter uma chamada correspondente à
btree_delete_h(BTree *bt).
-
void btree_init(BTree *bt, int order);- Inicializa uma B-Tree alocada na Stack.
- Deve ter uma chamada correspondente à
btree_delete_s(BTree *bt).
-
void btree_delete_h(BTree *bt);- Deleta uma B-Tree alocada na Heap. Deve ser chamada para não gerar memory leak.
-
void btree_delete_s(BTree *bt);- Deleta uma B-Tree alocada na Stack. Deve ser chamada para não gerar memory leak.
-
node_position btree_insert(BTree *bt, int key, void *value);- Insere a chave
keyembt, associando-a ao valorvalue. - Retorna o node_position da inserção ou (NULL, -1).
- Insere a chave
-
node_position btree_find(BTree* bt, int key);- Procura em
bta chavekey. - Retorna o node_position da busca ou (NULL, -1).
- Procura em
-
node_position btree_remove(BTree* bt, int key);- Remove de
bta chavekey. - Retorna o node_position da remoção ou (NULL, -1).
- Remove de
-
void btree_dfs(BTree *bt);- Executa uma DFS sobre
btimprimindo o conteúdo dos seus nós em ordem posfixa.
- Executa uma DFS sobre
BTree *tree = btree_new(2);
ou
BTree tree;
btree_init(tree, 2);
Pseudo-código:
Allocate-Node(Order, IsLeaf):
Node.order = Order
Node.isLeaf = IsLeaf
Node.nKeys = 0
Node.children = Allocate-Children-Pointers(2*Order)
Node.keys = Allocate-Keys-Pointers(2*Order-1)
return Node
B-Tree-Create(T, Order):
T.order = Order
T.root = Allocate-Node(Order, True)
node_position pos = btree_insert(tree, key, value);
B-Tree-Insert(T, Key, Order)
r = T.root
if r.nKeys == 2*Order -1:
s = Allocate-Node(Order, False)
T.root = s
s.c[0] = r
B-Tree-Split-Child(s, 1, Order)
B-Tree-Insert-Nonfull(s, Key, Order)
else:
B-Tree-Insert-Nonfull(r, Key, Order)
node_position pos = btree_find(tree, key);
node_position pos = btree_remove(tree, key);
btree_delete_s(tree);
ou
btree_delete_h(tree);
A deleção da B-Tree é executada aplicando-se a remoção sobre a primeira chave da raiz, enquanto existirem chaves.
Pseudo-Código:
while T.root.nKkeys > 0:
BTree-Remove(T, T.root.keys[0])
Apesar de a operação poder ser mais eficiente aplicando-se, por exemplo, uma DFS de deleção, também é garantido que a remoção eliminará todos os pair's e node's gerados na B-Tree.
Após o fim da remoção, deleta-se o nó raiz.
As duas funções diferem apenas em um passo:
- Invocar
btree_delete_h(tree)implica em desalocar também o ponteiro tree.