initial commit
This commit is contained in:
357
core/include/blue/core/btree.h
Normal file
357
core/include/blue/core/btree.h
Normal file
@@ -0,0 +1,357 @@
|
||||
#ifndef BLUELIB_CORE_BTREE_H_
|
||||
#define BLUELIB_CORE_BTREE_H_
|
||||
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/misc.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* defines a simple node insertion function.
|
||||
this function assumes that your nodes have simple integer keys that can be
|
||||
compared with the usual operators.
|
||||
|
||||
EXAMPLE:
|
||||
if you have a tree node type like this:
|
||||
|
||||
struct my_tree_node {
|
||||
int key;
|
||||
b_btree_node base;
|
||||
}
|
||||
|
||||
You would use the following call to generate an insert function for a tree
|
||||
with this node type:
|
||||
|
||||
BTREE_DEFINE_SIMPLE_INSERT(
|
||||
struct my_tree_node,
|
||||
base,
|
||||
key,
|
||||
my_tree_node_insert);
|
||||
|
||||
Which would emit a function defined like:
|
||||
|
||||
static void my_tree_node_insert(b_btree *tree, struct my_tree_node *node);
|
||||
|
||||
@param node_type your custom tree node type. usually a structure that
|
||||
contains a b_btree_node member.
|
||||
@param container_node_member the name of the b_btree_node member variable
|
||||
within your custom type.
|
||||
@param container_key_member the name of the key member variable within your
|
||||
custom type.
|
||||
@param function_name the name of the function to generate.
|
||||
*/
|
||||
#define B_BTREE_DEFINE_SIMPLE_INSERT( \
|
||||
node_type, container_node_member, container_key_member, function_name) \
|
||||
void function_name(b_btree *tree, node_type *node) \
|
||||
{ \
|
||||
if (!tree->b_root) { \
|
||||
tree->b_root = &node->container_node_member; \
|
||||
b_btree_insert_fixup(tree, &node->container_node_member); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
b_btree_node *cur = tree->b_root; \
|
||||
while (1) { \
|
||||
node_type *cur_node = b_unbox( \
|
||||
node_type, cur, container_node_member); \
|
||||
b_btree_node *next = NULL; \
|
||||
\
|
||||
if (node->container_key_member \
|
||||
>= cur_node->container_key_member) { \
|
||||
next = b_btree_right(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
b_btree_put_right( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else if ( \
|
||||
node->container_key_member \
|
||||
< cur_node->container_key_member) { \
|
||||
next = b_btree_left(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
b_btree_put_left( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
cur = next; \
|
||||
} \
|
||||
\
|
||||
b_btree_insert_fixup(tree, &node->container_node_member); \
|
||||
}
|
||||
|
||||
/* defines a node insertion function.
|
||||
this function should be used for trees with complex node keys that cannot be
|
||||
directly compared. a comparator for your keys must be supplied.
|
||||
|
||||
EXAMPLE:
|
||||
if you have a tree node type like this:
|
||||
|
||||
struct my_tree_node {
|
||||
complex_key_t key;
|
||||
b_btree_node base;
|
||||
}
|
||||
|
||||
You would need to define a comparator function or macro with the following
|
||||
signature:
|
||||
|
||||
int my_comparator(struct my_tree_node *a, struct my_tree_node *b);
|
||||
|
||||
Which implements the following:
|
||||
|
||||
return -1 if a < b
|
||||
return 0 if a == b
|
||||
return 1 if a > b
|
||||
|
||||
You would use the following call to generate an insert function for a tree
|
||||
with this node type:
|
||||
|
||||
BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert,
|
||||
my_comparator);
|
||||
|
||||
Which would emit a function defined like:
|
||||
|
||||
static void my_tree_node_insert(b_btree *tree, struct my_tree_node *node);
|
||||
|
||||
@param node_type your custom tree node type. usually a structure that
|
||||
contains a b_btree_node member.
|
||||
@param container_node_member the name of the b_btree_node member variable
|
||||
within your custom type.
|
||||
@param container_key_member the name of the key member variable within your
|
||||
custom type.
|
||||
@param function_name the name of the function to generate.
|
||||
@param comparator the name of a comparator function or functional-macro that
|
||||
conforms to the requirements listed above.
|
||||
*/
|
||||
#define B_BTREE_DEFINE_INSERT( \
|
||||
node_type, container_node_member, container_key_member, function_name, \
|
||||
comparator) \
|
||||
void function_name(b_btree *tree, node_type *node) \
|
||||
{ \
|
||||
if (!tree->b_root) { \
|
||||
tree->b_root = &node->container_node_member; \
|
||||
b_btree_insert_fixup(tree, &node->container_node_member); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
b_btree_node *cur = tree->b_root; \
|
||||
while (1) { \
|
||||
node_type *cur_node = b_unbox( \
|
||||
node_type, cur, container_node_member); \
|
||||
b_btree_node *next = NULL; \
|
||||
int cmp = comparator(node, cur_node); \
|
||||
\
|
||||
if (cmp >= 0) { \
|
||||
next = b_btree_right(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
b_btree_put_right( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else if (cmp == -1) { \
|
||||
next = b_btree_left(cur); \
|
||||
\
|
||||
if (!next) { \
|
||||
b_btree_put_left( \
|
||||
cur, \
|
||||
&node->container_node_member); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
cur = next; \
|
||||
} \
|
||||
\
|
||||
b_btree_insert_fixup(tree, &node->container_node_member); \
|
||||
}
|
||||
|
||||
/* defines a simple tree search function.
|
||||
this function assumes that your nodes have simple integer keys that can be
|
||||
compared with the usual operators.
|
||||
|
||||
EXAMPLE:
|
||||
if you have a tree node type like this:
|
||||
|
||||
struct my_tree_node {
|
||||
int key;
|
||||
b_btree_node base;
|
||||
}
|
||||
|
||||
You would use the following call to generate a search function for a tree
|
||||
with this node type:
|
||||
|
||||
BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key,
|
||||
my_tree_node_get);
|
||||
|
||||
Which would emit a function defined like:
|
||||
|
||||
static struct my_tree_node *my_tree_node_get(b_btree *tree, int key);
|
||||
|
||||
@param node_type your custom tree node type. usually a structure that
|
||||
contains a b_btree_node member.
|
||||
@param key_type the type name of the key embedded in your custom tree node
|
||||
type. this type must be compatible with the builtin comparison operators.
|
||||
@param container_node_member the name of the b_btree_node member variable
|
||||
within your custom type.
|
||||
@param container_key_member the name of the key member variable within your
|
||||
custom type.
|
||||
@param function_name the name of the function to generate.
|
||||
*/
|
||||
#define B_BTREE_DEFINE_SIMPLE_GET( \
|
||||
node_type, key_type, container_node_member, container_key_member, \
|
||||
function_name) \
|
||||
node_type *function_name(const b_btree *tree, key_type key) \
|
||||
{ \
|
||||
b_btree_node *cur = tree->b_root; \
|
||||
while (cur) { \
|
||||
node_type *cur_node = b_unbox( \
|
||||
node_type, cur, container_node_member); \
|
||||
if (key > cur_node->container_key_member) { \
|
||||
cur = b_btree_right(cur); \
|
||||
} else if (key < cur_node->container_key_member) { \
|
||||
cur = b_btree_left(cur); \
|
||||
} else { \
|
||||
return cur_node; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define b_btree_foreach(it, btree) \
|
||||
for (int z__b_unique_name() = b_btree_iterator_begin(btree, it); \
|
||||
(it)->node != NULL; b_btree_iterator_next(it))
|
||||
|
||||
/* binary tree nodes. this *cannot* be used directly. you need to define a
|
||||
custom node type that contains a member variable of type b_btree_node.
|
||||
|
||||
you would then use the supplied macros to define functions to manipulate your
|
||||
custom binary tree.
|
||||
*/
|
||||
typedef struct b_btree_node {
|
||||
struct b_btree_node *b_parent, *b_left, *b_right;
|
||||
unsigned short b_height;
|
||||
} b_btree_node;
|
||||
|
||||
/* binary tree. unlike b_btree_node, you can define variables of type b_btree.
|
||||
*/
|
||||
typedef struct b_btree {
|
||||
b_btree_node *b_root;
|
||||
} b_btree;
|
||||
|
||||
typedef struct b_btree_iterator {
|
||||
b_iterator _base;
|
||||
size_t i, depth;
|
||||
b_btree_node *node;
|
||||
b_btree *_b;
|
||||
} b_btree_iterator;
|
||||
|
||||
/* re-balance a binary tree after an insertion operation.
|
||||
|
||||
NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or
|
||||
similar, this function will automatically called for you.
|
||||
|
||||
@param tree the tree to re-balance.
|
||||
@param node the node that was just inserted into the tree.
|
||||
*/
|
||||
extern void b_btree_insert_fixup(b_btree *tree, b_btree_node *node);
|
||||
|
||||
/* delete a node from a binary tree and re-balance the tree afterwards.
|
||||
|
||||
@param tree the tree to delete from
|
||||
@param node the node to delete.
|
||||
*/
|
||||
extern void b_btree_delete(b_btree *tree, b_btree_node *node);
|
||||
|
||||
/* get the first node in a binary tree.
|
||||
|
||||
this will be the node with the smallest key (i.e. the node that is
|
||||
furthest-left from the root)
|
||||
*/
|
||||
extern b_btree_node *b_btree_first(const b_btree *tree);
|
||||
|
||||
/* get the last node in a binary tree.
|
||||
|
||||
this will be the node with the largest key (i.e. the node that is
|
||||
furthest-right from the root)
|
||||
*/
|
||||
extern b_btree_node *b_btree_last(const b_btree *tree);
|
||||
/* for any binary tree node, this function returns the node with the
|
||||
* next-largest key value */
|
||||
extern b_btree_node *b_btree_next(const b_btree_node *node);
|
||||
/* for any binary tree node, this function returns the node with the
|
||||
* next-smallest key value */
|
||||
extern b_btree_node *b_btree_prev(const b_btree_node *node);
|
||||
/* return true if the btree is empty, false otherwise */
|
||||
static inline bool b_btree_empty(const b_btree *tree)
|
||||
{
|
||||
return tree->b_root == NULL;
|
||||
}
|
||||
|
||||
/* sets `child` as the immediate left-child of `parent` */
|
||||
static inline void b_btree_put_left(b_btree_node *parent, b_btree_node *child)
|
||||
{
|
||||
parent->b_left = child;
|
||||
child->b_parent = parent;
|
||||
}
|
||||
|
||||
/* sets `child` as the immediate right-child of `parent` */
|
||||
static inline void b_btree_put_right(b_btree_node *parent, b_btree_node *child)
|
||||
{
|
||||
parent->b_right = child;
|
||||
child->b_parent = parent;
|
||||
}
|
||||
|
||||
/* get the immediate left-child of `node` */
|
||||
static inline b_btree_node *b_btree_left(b_btree_node *node)
|
||||
{
|
||||
return node->b_left;
|
||||
}
|
||||
|
||||
/* get the immediate right-child of `node` */
|
||||
static inline b_btree_node *b_btree_right(b_btree_node *node)
|
||||
{
|
||||
return node->b_right;
|
||||
}
|
||||
|
||||
/* get the immediate parent of `node` */
|
||||
static inline b_btree_node *b_btree_parent(b_btree_node *node)
|
||||
{
|
||||
return node->b_parent;
|
||||
}
|
||||
|
||||
/* get the height of `node`.
|
||||
|
||||
the height of a node is defined as the length of the longest path
|
||||
between the node and a leaf node.
|
||||
|
||||
this count includes the node itself, so the height of a leaf node will be 1.
|
||||
*/
|
||||
static inline unsigned short b_btree_height(b_btree_node *node)
|
||||
{
|
||||
return node->b_height;
|
||||
}
|
||||
|
||||
extern int b_btree_iterator_begin(const b_btree *tree, b_btree_iterator *it);
|
||||
extern bool b_btree_iterator_next(b_btree_iterator *it);
|
||||
extern b_status b_btree_iterator_erase(b_btree_iterator *it);
|
||||
extern bool b_btree_iterator_is_valid(const b_btree_iterator *it);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user