Files

892 lines
20 KiB
C
Raw Permalink Normal View History

2024-08-03 07:54:28 +01:00
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
/* templated AVL binary tree implementation
this file implements an extensible AVL binary tree data structure.
the primary rule of an AVL binary tree is that for a given node N,
the heights of N's left and right subtrees can differ by at most 1.
the height of a subtree is the length of the longest path between
the root of the subtree and a leaf node, including the root node itself.
the height of a leaf node is 1.
when a node is inserted into or deleted from the tree, this rule may
be broken, in which the tree must be rotated to restore the balance.
no more than one rotation is required for any insert operations,
while multiple rotations may be required for a delete operation.
there are four types of rotations that can be applied to a tree:
- left rotation
- right rotation
- double left rotations
- double right rotations
by enforcing the balance rule, for a tree with n nodes, the worst-case
performance for insert, delete, and search operations is guaranteed
to be O(log n).
this file intentionally excludes any kind of search function implementation.
it is up to the programmer to implement their own tree node type
using b_btree_node, and their own search function using b_btree.
this allows the programmer to define their own node types with complex
non-integer key types. btree.h contains a number of macros to help
define these functions. the macros do all the work, you just have to
provide a comparator function.
*/
#include <blue/core/btree.h>
#include <stddef.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c)))
#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c)))
#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left))
#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right))
#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right))
#define HAS_ONE_CHILD(x) \
((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \
|| (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)))
#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))
#define HEIGHT(x) ((x) ? (x)->b_height : 0)
struct b_btree_iterator_p {
size_t i, depth;
b_btree_node *node;
b_btree *_b;
};
2024-08-03 07:54:28 +01:00
static inline void update_height(struct b_btree_node *x)
{
x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1;
}
static inline int bf(struct b_btree_node *x)
{
int bf = 0;
if (!x) {
return bf;
}
if (x->b_right) {
bf += x->b_right->b_height;
}
if (x->b_left) {
bf -= x->b_left->b_height;
}
return bf;
}
/* perform a left rotation on a subtree
if you have a tree like this:
Z
/ \
X .
/ \
. Y
/ \
. .
and you perform a left rotation on node X,
you will get the following tree:
Z
/ \
Y .
/ \
X .
/ \
. .
note that this function does NOT update b_height for the rotated
nodes. it is up to you to call update_height_to_root().
*/
static void rotate_left(struct b_btree *tree, struct b_btree_node *x)
{
struct b_btree_node *y = x->b_right;
struct b_btree_node *p = x->b_parent;
if (y->b_left) {
y->b_left->b_parent = x;
}
x->b_right = y->b_left;
if (!p) {
tree->b_root = y;
} else if (x == p->b_left) {
p->b_left = y;
} else {
p->b_right = y;
}
x->b_parent = y;
y->b_left = x;
y->b_parent = p;
}
static void update_height_to_root(struct b_btree_node *x)
{
while (x) {
update_height(x);
x = x->b_parent;
}
}
/* perform a right rotation on a subtree
if you have a tree like this:
Z
/ \
. X
/ \
Y .
/ \
. .
and you perform a right rotation on node X,
you will get the following tree:
Z
/ \
. Y
/ \
. X
/ \
. .
note that this function does NOT update b_height for the rotated
nodes. it is up to you to call update_height_to_root().
*/
static void rotate_right(struct b_btree *tree, struct b_btree_node *y)
{
struct b_btree_node *x = y->b_left;
struct b_btree_node *p = y->b_parent;
if (x->b_right) {
x->b_right->b_parent = y;
}
y->b_left = x->b_right;
if (!p) {
tree->b_root = x;
} else if (y == p->b_left) {
p->b_left = x;
} else {
p->b_right = x;
}
y->b_parent = x;
x->b_right = y;
x->b_parent = p;
}
/* for a given node Z, perform a right rotation on Z's right child,
followed by a left rotation on Z itself.
if you have a tree like this:
Z
/ \
. X
/ \
Y .
/ \
. .
and you perform a double-left rotation on node Z,
you will get the following tree:
Y
/ \
/ \
Z X
/ \ / \
. . . .
note that, unlike rotate_left and rotate_right, this function
DOES update b_height for the rotated nodes (since it needs to be
done in a certain order).
*/
static void rotate_double_left(struct b_btree *tree, struct b_btree_node *z)
{
struct b_btree_node *x = z->b_right;
struct b_btree_node *y = x->b_left;
rotate_right(tree, x);
rotate_left(tree, z);
update_height(z);
update_height(x);
while (y) {
update_height(y);
y = y->b_parent;
}
}
/* for a given node Z, perform a left rotation on Z's left child,
followed by a right rotation on Z itself.
if you have a tree like this:
Z
/ \
X .
/ \
. Y
/ \
. .
and you perform a double-right rotation on node Z,
you will get the following tree:
Y
/ \
/ \
X Z
/ \ / \
. . . .
note that, unlike rotate_left and rotate_right, this function
DOES update b_height for the rotated nodes (since it needs to be
done in a certain order).
*/
static void rotate_double_right(struct b_btree *tree, struct b_btree_node *z)
{
struct b_btree_node *x = z->b_left;
struct b_btree_node *y = x->b_right;
rotate_left(tree, x);
rotate_right(tree, z);
update_height(z);
update_height(x);
while (y) {
update_height(y);
y = y->b_parent;
}
}
/* run after an insert operation. checks that the balance factor
of the local subtree is within the range -1 <= BF <= 1. if it
is not, rotate the subtree to restore balance.
note that at most one rotation should be required after a node
is inserted into the tree.
this function depends on all nodes in the tree having
correct b_height values.
@param w the node that was just inserted into the tree
*/
static void insert_fixup(struct b_btree *tree, struct b_btree_node *w)
{
struct b_btree_node *z = NULL, *y = NULL, *x = NULL;
z = w;
while (z) {
if (bf(z) >= -1 && bf(z) <= 1) {
goto next_ancestor;
}
if (IS_LEFT_CHILD(z, y)) {
if (IS_LEFT_CHILD(y, x)) {
rotate_right(tree, z);
update_height_to_root(z);
} else {
rotate_double_right(tree, z);
}
} else {
if (IS_LEFT_CHILD(y, x)) {
rotate_double_left(tree, z);
} else {
rotate_left(tree, z);
update_height_to_root(z);
}
}
next_ancestor:
x = y;
y = z;
z = z->b_parent;
}
}
/* run after a delete operation. checks that the balance factor
of the local subtree is within the range -1 <= BF <= 1. if it
is not, rotate the subtree to restore balance.
note that, unlike insert_fixup, multiple rotations may be required
to restore balance after a node is deleted.
this function depends on all nodes in the tree having
correct b_height values.
@param w one of the following:
- the parent of the node that was deleted if the node
had no children.
- the parent of the node that replaced the deleted node
if the deleted node had two children.
- the node that replaced the node that was deleted, if
the node that was deleted had one child.
*/
static void delete_fixup(struct b_btree *tree, struct b_btree_node *w)
{
struct b_btree_node *z = w;
while (z) {
if (bf(z) > 1) {
if (bf(z->b_right) >= 0) {
rotate_left(tree, z);
update_height_to_root(z);
} else {
rotate_double_left(tree, z);
}
} else if (bf(z) < -1) {
if (bf(z->b_left) <= 0) {
rotate_right(tree, z);
update_height_to_root(z);
} else {
rotate_double_right(tree, z);
}
}
z = z->b_parent;
}
}
/* updates b_height for all nodes between the inserted node and the root
of the tree, and calls insert_fixup.
@param node the node that was just inserted into the tree.
*/
void b_btree_insert_fixup(struct b_btree *tree, struct b_btree_node *node)
{
node->b_height = 0;
struct b_btree_node *cur = node;
while (cur) {
update_height(cur);
cur = cur->b_parent;
}
insert_fixup(tree, node);
}
/* remove a node from a tree.
this function assumes that `node` has no children, and therefore
doesn't need to be replaced.
updates b_height for all nodes between `node` and the tree root.
@param node the node to delete.
*/
static struct b_btree_node *remove_node_with_no_children(
struct b_btree *tree, struct b_btree_node *node)
2024-08-03 07:54:28 +01:00
{
struct b_btree_node *w = node->b_parent;
struct b_btree_node *p = node->b_parent;
node->b_parent = NULL;
if (!p) {
tree->b_root = NULL;
} else if (IS_LEFT_CHILD(p, node)) {
p->b_left = NULL;
} else {
p->b_right = NULL;
}
while (p) {
update_height(p);
p = p->b_parent;
}
return w;
}
/* remove a node from a tree.
this function assumes that `node` has one child.
the child of `node` is inherited by `node`'s parent, and `node` is removed.
updates b_height for all nodes between the node that replaced
`node` and the tree root.
@param node the node to delete.
*/
static struct b_btree_node *replace_node_with_one_subtree(
struct b_btree *tree, struct b_btree_node *node)
2024-08-03 07:54:28 +01:00
{
struct b_btree_node *p = node->b_parent;
struct b_btree_node *z = NULL;
if (HAS_LEFT_CHILD(node)) {
z = node->b_left;
} else {
z = node->b_right;
}
struct b_btree_node *w = z;
if (!p) {
tree->b_root = z;
} else if (IS_LEFT_CHILD(p, node)) {
p->b_left = z;
} else if (IS_RIGHT_CHILD(p, node)) {
p->b_right = z;
}
z->b_parent = p;
node->b_parent = NULL;
node->b_left = node->b_right = NULL;
while (z) {
update_height(z);
z = z->b_parent;
}
return w;
}
/* remove a node from a tree.
this function assumes that `node` has two children.
find the in-order successor Y of `node` (the largest node in `node`'s left
sub-tree), removes `node` from the tree and moves Y to where `node` used to
be.
if Y has a child (it will never have more than one), have Y's parent inherit
Y's child.
updates b_height for all nodes between the deepest node that was modified
and the tree root.
@param z the node to delete.
*/
static struct b_btree_node *replace_node_with_two_subtrees(
struct b_btree *tree, struct b_btree_node *z)
2024-08-03 07:54:28 +01:00
{
/* x will replace z */
struct b_btree_node *x = z->b_left;
while (x->b_right) {
x = x->b_right;
}
/* y is the node that will replace x (if x has a left child) */
struct b_btree_node *y = x->b_left;
/* w is the starting point for the height update and fixup */
struct b_btree_node *w = x;
if (w->b_parent != z) {
w = w->b_parent;
}
if (y) {
w = y;
}
if (IS_LEFT_CHILD(x->b_parent, x)) {
x->b_parent->b_left = y;
} else if (IS_RIGHT_CHILD(x->b_parent, x)) {
x->b_parent->b_right = y;
}
if (y) {
y->b_parent = x->b_parent;
}
if (IS_LEFT_CHILD(z->b_parent, z)) {
z->b_parent->b_left = x;
} else if (IS_RIGHT_CHILD(z->b_parent, z)) {
z->b_parent->b_right = x;
}
x->b_parent = z->b_parent;
x->b_left = z->b_left;
x->b_right = z->b_right;
if (x->b_left) {
x->b_left->b_parent = x;
}
if (x->b_right) {
x->b_right->b_parent = x;
}
if (!x->b_parent) {
tree->b_root = x;
}
struct b_btree_node *cur = w;
while (cur) {
update_height(cur);
cur = cur->b_parent;
}
return w;
}
/* delete a node from the tree and re-balance it afterwards */
void b_btree_delete(struct b_btree *tree, struct b_btree_node *node)
{
struct b_btree_node *w = NULL;
if (HAS_NO_CHILDREN(node)) {
w = remove_node_with_no_children(tree, node);
} else if (HAS_ONE_CHILD(node)) {
w = replace_node_with_one_subtree(tree, node);
} else if (HAS_TWO_CHILDREN(node)) {
w = replace_node_with_two_subtrees(tree, node);
}
if (w) {
delete_fixup(tree, w);
}
node->b_left = node->b_right = node->b_parent = NULL;
}
static struct b_btree_node *first_node(const struct b_btree *tree, int *depth)
{
/* the first node in the tree is the node with the smallest key.
we keep moving left until we can't go any further */
struct b_btree_node *cur = tree->b_root;
int d = 0;
if (!cur) {
*depth = 0;
return NULL;
}
while (cur->b_left) {
d++;
cur = cur->b_left;
}
*depth = d;
return cur;
}
struct b_btree_node *b_btree_first(const struct b_btree *tree)
{
int d;
return first_node(tree, &d);
}
static struct b_btree_node *last_node(const struct b_btree *tree, int *depth)
{
/* the first node in the tree is the node with the largest key.
we keep moving right until we can't go any further */
struct b_btree_node *cur = tree->b_root;
int d = 0;
if (!cur) {
return NULL;
}
while (cur->b_right) {
d++;
cur = cur->b_right;
}
*depth = d;
return cur;
}
b_btree_node *b_btree_last(const struct b_btree *tree)
{
int d;
return last_node(tree, &d);
}
static b_btree_node *next_node(const struct b_btree_node *node, int *depth_diff)
{
if (!node) {
return NULL;
}
int depth = 0;
/* there are two possibilities for the next node:
1. if `node` has a right sub-tree, every node in this sub-tree is
bigger than node. the in-order successor of `node` is the smallest
node in this subtree.
2. if `node` has no right sub-tree, we've reached the largest node in
the sub-tree rooted at `node`. we need to go back to our parent
and continue the search elsewhere.
*/
if (node->b_right) {
/* case 1: step into `node`'s right sub-tree and keep going
left to find the smallest node */
struct b_btree_node *cur = node->b_right;
depth++;
while (cur->b_left) {
cur = cur->b_left;
depth++;
}
*depth_diff = depth;
return cur;
}
/* case 2: keep stepping back up towards the root of the tree.
if we encounter a step where we are our parent's left child,
we've found a parent with a value larger than us. this parent
is the in-order successor of `node` */
while (node->b_parent && node->b_parent->b_left != node) {
node = node->b_parent;
depth--;
}
*depth_diff = depth - 1;
return node->b_parent;
}
static b_btree_node *prev_node(const struct b_btree_node *node, int *depth_diff)
{
if (!node) {
return NULL;
}
int depth = 0;
/* there are two possibilities for the previous node:
1. if `node` has a left sub-tree, every node in this sub-tree is
smaller than `node`. the in-order predecessor of `node` is the
largest node in this subtree.
2. if `node` has no left sub-tree, we've reached the smallest node in
the sub-tree rooted at `node`. we need to go back to our parent
and continue the search elsewhere.
*/
if (node->b_left) {
/* case 1: step into `node`'s left sub-tree and keep going
right to find the largest node */
b_btree_node *cur = node->b_left;
depth++;
while (cur->b_right) {
cur = cur->b_right;
depth++;
}
*depth_diff = depth;
return cur;
}
/* case 2: keep stepping back up towards the root of the tree.
if we encounter a step where we are our parent's right child,
we've found a parent with a value smaller than us. this parent
is the in-order predecessor of `node`. */
while (node->b_parent && node->b_parent->b_right != node) {
node = node->b_parent;
depth--;
}
*depth_diff = depth - 1;
return node->b_parent;
}
b_btree_node *b_btree_next(const struct b_btree_node *node)
{
int d;
return next_node(node, &d);
}
b_btree_node *b_btree_prev(const struct b_btree_node *node)
{
int d;
return prev_node(node, &d);
}
void b_btree_move(
struct b_btree *tree, struct b_btree_node *dest, struct b_btree_node *src)
{
if (src->b_parent) {
if (src->b_parent->b_left == src) {
src->b_parent->b_left = dest;
} else {
src->b_parent->b_right = dest;
}
}
if (src->b_left) {
src->b_left->b_parent = dest;
}
if (src->b_right) {
src->b_right->b_parent = dest;
}
if (tree->b_root == src) {
tree->b_root = dest;
}
memmove(dest, src, sizeof *src);
}
b_iterator *b_btree_begin(struct b_btree *tree)
2024-08-03 07:54:28 +01:00
{
b_iterator *it_obj = b_object_create(B_TYPE_BTREE_ITERATOR);
if (!it_obj) {
return NULL;
}
2024-08-03 07:54:28 +01:00
struct b_btree_iterator_p *it
= b_object_get_private(it_obj, B_TYPE_BTREE_ITERATOR);
int depth = 0;
2024-08-03 07:54:28 +01:00
it->_b = (struct b_btree *)tree;
it->i = 0;
it->node = first_node(tree, &depth);
it->depth = depth;
2024-08-03 07:54:28 +01:00
return it_obj;
}
2024-08-03 07:54:28 +01:00
const b_iterator *b_btree_cbegin(const struct b_btree *tree)
2024-08-03 07:54:28 +01:00
{
b_iterator *it_obj = b_object_create(B_TYPE_BTREE_ITERATOR);
if (!it_obj) {
return NULL;
}
struct b_btree_iterator_p *it
= b_object_get_private(it_obj, B_TYPE_BTREE_ITERATOR);
2024-08-03 07:54:28 +01:00
int depth = 0;
it->_b = (struct b_btree *)tree;
it->i = 0;
it->node = first_node(tree, &depth);
it->depth = depth;
return it_obj;
2024-08-03 07:54:28 +01:00
}
static enum b_status iterator_move_next(const b_iterator *obj)
2024-08-03 07:54:28 +01:00
{
struct b_btree_iterator_p *it
= b_object_get_private(obj, B_TYPE_BTREE_ITERATOR);
2024-08-03 07:54:28 +01:00
int depth_diff = 0;
struct b_btree_node *next = next_node(it->node, &depth_diff);
if (!next) {
it->node = NULL;
it->depth = 0;
it->i++;
return false;
}
it->node = next;
it->i++;
it->depth += depth_diff;
return true;
}
static enum b_status iterator_erase(b_iterator *obj)
2024-08-03 07:54:28 +01:00
{
struct b_btree_iterator_p *it
= b_object_get_private(obj, B_TYPE_BTREE_ITERATOR);
2024-08-03 07:54:28 +01:00
if (!it->node) {
return B_ERR_OUT_OF_BOUNDS;
}
int depth_diff = 0;
struct b_btree_node *next = next_node(it->node, &depth_diff);
b_btree_delete(it->_b, it->node);
if (!next) {
it->node = NULL;
it->depth = 0;
} else {
it->node = next;
it->depth = 0;
struct b_btree_node *cur = next->b_parent;
while (cur) {
it->depth++;
cur = cur->b_parent;
}
}
return B_SUCCESS;
}
static b_iterator_value iterator_get_value(b_iterator *obj)
2024-08-03 07:54:28 +01:00
{
struct b_btree_iterator_p *it
= b_object_get_private(obj, B_TYPE_BTREE_ITERATOR);
return B_ITERATOR_VALUE_PTR(it->node);
}
static const b_iterator_value iterator_get_cvalue(const b_iterator *obj)
{
struct b_btree_iterator_p *it
= b_object_get_private(obj, B_TYPE_BTREE_ITERATOR);
return B_ITERATOR_VALUE_CPTR(it->node);
2024-08-03 07:54:28 +01:00
}
/*** CLASS DEFINITION *********************************************************/
// ---- b_btree_iterator DEFINITION
B_TYPE_CLASS_DEFINITION_BEGIN(b_btree_iterator)
B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT)
B_INTERFACE_ENTRY(to_string) = NULL;
B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT)
B_TYPE_CLASS_INTERFACE_BEGIN(b_iterator, B_TYPE_ITERATOR)
B_INTERFACE_ENTRY(it_move_next) = iterator_move_next;
B_INTERFACE_ENTRY(it_erase) = iterator_erase;
B_INTERFACE_ENTRY(it_get_value) = iterator_get_value;
B_INTERFACE_ENTRY(it_get_cvalue) = iterator_get_cvalue;
B_TYPE_CLASS_INTERFACE_END(b_iterator, B_TYPE_ITERATOR)
B_TYPE_CLASS_DEFINITION_END(b_btree_iterator)
B_TYPE_DEFINITION_BEGIN(b_btree_iterator)
B_TYPE_ID(0x432779d7, 0xc03a, 0x48ea, 0xae8f, 0x12c666c767ae);
B_TYPE_EXTENDS(B_TYPE_ITERATOR);
B_TYPE_CLASS(b_btree_iterator_class);
B_TYPE_INSTANCE_PRIVATE(struct b_btree_iterator_p);
B_TYPE_DEFINITION_END(b_btree_iterator)