/* 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 #include #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) 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) { 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) { 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) { /* 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); } static bool btree_iterator_next(struct b_iterator *it) { return b_btree_iterator_next((struct b_btree_iterator *)it); } static b_status btree_iterator_erase(struct b_iterator *it) { return b_btree_iterator_erase((struct b_btree_iterator *)it); } static bool btree_iterator_is_valid(const struct b_iterator *it) { return b_btree_iterator_is_valid((struct b_btree_iterator *)it); } static const b_iterator_ops btree_iterator_ops = { .it_next = btree_iterator_next, .it_erase = btree_iterator_erase, .it_close = NULL, .it_is_valid = btree_iterator_is_valid, }; int b_btree_iterator_begin( const struct b_btree *tree, struct b_btree_iterator *it) { int depth = 0; it->_b = (struct b_btree *)tree; it->i = 0; it->node = first_node(tree, &depth); it->depth = depth; it->_base.it_ops = &btree_iterator_ops; return 0; } bool b_btree_iterator_next(struct b_btree_iterator *it) { 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; } b_status b_btree_iterator_erase(struct b_btree_iterator *it) { 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; } bool b_btree_iterator_is_valid(const struct b_btree_iterator *it) { return it->node != NULL; }