From 0e9f8d615add96ba23a86871e698f8a02f1cf010 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Tue, 24 Nov 2020 19:42:16 +0100 Subject: [PATCH] tupperware: add intrusive AVL tree --- Makefile | 1 + include/tupperware/avl.h | 66 +++++++ src/avl.c | 418 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 485 insertions(+) create mode 100644 include/tupperware/avl.h create mode 100644 src/avl.c diff --git a/Makefile b/Makefile index 5e765d8..95ed1ef 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ CPPFLAGS = -Iinclude/ -Itests/ VPATH = src/ tests/ SRC = \ + src/avl.c \ src/list.c \ OBJS = $(SRC:.c=.o) diff --git a/include/tupperware/avl.h b/include/tupperware/avl.h new file mode 100644 index 0000000..6e19f1d --- /dev/null +++ b/include/tupperware/avl.h @@ -0,0 +1,66 @@ +#ifndef TUPPERWARE_AVL_H +#define TUPPERWARE_AVL_H + +#include +#include + +struct avl_node { + struct avl_node *left; + struct avl_node *right; + int balance; +}; + +typedef int (*avl_cmp_f)(const struct avl_node *lhs, + const struct avl_node *rhs, void *cookie); +typedef void (*avl_map_f)(struct avl_node *n, void *cookie); +typedef bool (*avl_filter_f)(struct avl_node *n, void *cookie); + +struct avl { + avl_cmp_f cmp; + void *cookie; + struct avl_node *root; +}; + +#define CONTAINER_OF(Type, Field, Ptr) \ + ((Type*)((char*)(Ptr) - offsetof(Type, Field))) + +#define AVL_NODE_INIT_VAL \ + ((struct avl_node){ \ + .left = NULL, \ + .right = NULL, \ + .balance = 0, \ + }) + +void avl_init(struct avl *tree, avl_cmp_f cmp, void *cookie); +void avl_clear(struct avl *tree, + void (*dtor)(struct avl_node *n, void *cookie), void *cookie); + +bool avl_insert(struct avl *tree, + struct avl_node *v, struct avl_node **inserted); +struct avl_node *avl_insert_or_update(struct avl *tree, struct avl_node *v); +void avl_insert_multi(struct avl *tree, struct avl_node *v); + +struct avl_node *avl_remove(struct avl *tree, struct avl_node *v); +bool avl_remove_at(struct avl *tree, struct avl_node *v); + +struct avl_node *avl_find(const struct avl *tree, const struct avl_node *v); +struct avl_node *avl_lower_bound(const struct avl *tree, + const struct avl_node *v); +struct avl_node *avl_upper_bound(const struct avl *tree, + const struct avl_node *v); + +bool avl_empty(const struct avl *tree); +size_t avl_size(const struct avl *tree); +size_t avl_height(const struct avl *tree); + +void avl_merge(struct avl *tree, struct avl *more); +void avl_update(struct avl *tree, struct avl *more); +void avl_merge_all(struct avl *tree, struct avl *more); + +void avl_prefix_map(struct avl *tree, avl_map_f map, void *cookie); +void avl_infix_map(struct avl *tree, avl_map_f map, void *cookie); +void avl_postfix_map(struct avl *tree, avl_map_f map, void *cookie); +void avl_map_between(const struct avl *tree, + struct avl_node *inter[2], avl_map_f map, void *cookie); + +#endif /* !TUPPERWARE_AVL_H */ diff --git a/src/avl.c b/src/avl.c new file mode 100644 index 0000000..4f4018e --- /dev/null +++ b/src/avl.c @@ -0,0 +1,418 @@ +#include "tupperware/avl.h" + +#include + +#define AVL_NO_CHANGE (INT_MIN) + +void avl_init(struct avl *tree, avl_cmp_f cmp, void *cookie) { + if (!tree) + return; + tree->root = NULL; + tree->cmp = cmp; + tree->cookie = cookie; +} + +static void avl_clear_helper(struct avl_node *n, + void (*dtor)(struct avl_node *n, void *cookie), void *cookie) { + if (!n) + return; + + avl_clear_helper(n->left, dtor, cookie); + avl_clear_helper(n->right, dtor, cookie); + + n->left = NULL; + n->right = NULL; + + dtor(n, cookie); +} + +void avl_clear(struct avl *tree, + void (*dtor)(struct avl_node *n, void *cookie), void *cookie) { + if (!tree) + return; + avl_clear_helper(tree->root, dtor, cookie); +} + +static void left_rotate(struct avl_node **r) { + struct avl_node *rhs = (*r)->right; + (*r)->right = rhs->left; + rhs->left = *r; + *r = rhs; + + rhs->left->balance = -1 - rhs->balance; + rhs->balance = -rhs->left->balance; +} + +static void right_rotate(struct avl_node **r) { + struct avl_node *lhs = (*r)->left; + (*r)->left = lhs->right; + lhs->right = *r; + *r = lhs; + + lhs->right->balance = 1 - lhs->balance; + lhs->balance = -lhs->right->balance; +} + +static void left_right_rotate(struct avl_node **r) { + struct avl_node *gc = (*r)->left->right; + (*r)->left->right = gc->left; + gc->left = (*r)->left; + (*r)->left = gc->right; + gc->right = *r; + *r = gc; + + gc->left->balance = (gc->balance * (gc->balance - 1)) >> 1; + gc->right->balance = - (gc->balance * (gc->balance + 1)) >> 1; + gc->balance = 0; +} + +static void right_left_rotate(struct avl_node **r) { + struct avl_node *gc = (*r)->right->left; + (*r)->right->left = gc->right; + gc->right = (*r)->right; + (*r)->right = gc->left; + gc->left = *r; + *r = gc; + + gc->left->balance = (gc->balance * (gc->balance - 1)) >> 1; + gc->right->balance = - (gc->balance * (gc->balance + 1)) >> 1; + gc->balance = 0; +} + +static int rebalance(struct avl_node **r) { + switch ((*r)->balance) { + case -2: + if ((*r)->right->balance == 1) + right_left_rotate(r); + else + left_rotate(r); + break; + case 2: + if ((*r)->left->balance == -1) + left_right_rotate(r); + else + right_rotate(r); + break; + } + return (*r)->balance; +} + +static void avl_detach_node(struct avl_node *n) { + n->left = NULL; + n->right = NULL; + n->balance = 0; +} + +static void avl_steal_node(struct avl_node *n1, struct avl_node *n2) { + n1->left = n2->left; + n1->right = n2->right; + n1->balance = n2->balance; + avl_detach_node(n2); +} + +struct insert_parameters { + avl_cmp_f cmp; + void *cookie; + enum { ALLOW_MULTI, KEEP_OLD, REPLACE_OLD, } policy; +}; + +static int avl_insert_help(struct avl_node **r, struct avl_node *v, + struct avl_node **inserted, const struct insert_parameters *params) { + if (*r == NULL) { + *r = v; + v->balance = 0; + v->left = NULL; + v->right = NULL; + *inserted = v; + return 1; + } + + int c = params->cmp(v, *r, params->cookie); + if (c == 0 && params->policy != ALLOW_MULTI) { + *inserted = *r; + if (params->policy == REPLACE_OLD) { + avl_steal_node(v, *r); + *r = v; + } + return AVL_NO_CHANGE; + } + + struct avl_node **target = (c < 0 ? &((*r)->left) : &((*r)->right)); + int dir = (c < 0 ? 1 : -1); + + int tmp = avl_insert_help(target, v, inserted, params); + if (tmp == 0 || tmp == AVL_NO_CHANGE) + return tmp; + + (*r)->balance += dir; + + return rebalance(r); +} + +bool avl_insert(struct avl *tree, + struct avl_node *v, struct avl_node **inserted) { + if (!tree || !v) + return false; + + struct insert_parameters params = { + .cmp = tree->cmp, + .cookie = tree->cookie, + .policy = KEEP_OLD, + }; + struct avl_node *inserted_int = NULL; + int ret = avl_insert_help(&tree->root, v, &inserted_int, ¶ms); + + if (inserted) + *inserted = inserted_int; + + return ret != AVL_NO_CHANGE; +} + +struct avl_node *avl_insert_or_update(struct avl *tree, struct avl_node *v) { + if (!tree || !v) + return NULL; + + struct avl_node *previous_node = NULL; + struct insert_parameters params = { + .cmp = tree->cmp, + .cookie = tree->cookie, + .policy = REPLACE_OLD, + }; + + avl_insert_help(&tree->root, v, &previous_node, ¶ms); + + return (previous_node == v ? NULL : previous_node); +} + +void avl_insert_multi(struct avl *tree, struct avl_node *v) { + if (!tree || !v) + return; + + struct insert_parameters params = { + .cmp = tree->cmp, + .cookie = tree->cookie, + .policy = ALLOW_MULTI, + }; + struct avl_node *dummy = NULL; + avl_insert_help(&tree->root, v, &dummy, ¶ms); +} + +static int avl_remove_min(struct avl_node **r, struct avl_node **target) { + if ((*r)->left == NULL) { + *target = *r; + *r = (*target)->right; + return 1; + } + if (avl_remove_min(&((*r)->left), target) == 0) + return 0; + (*r)->balance -= 1; + int bal = rebalance(r); + return 1 - bal * bal; +} + +static int avl_remove_max(struct avl_node **r, struct avl_node **target) { + if ((*r)->right == NULL) { + *target = *r; + *r = (*target)->right; + return 1; + } + if (avl_remove_max(&((*r)->right), target) == 0) + return 0; + (*r)->balance -= 1; + int bal = rebalance(r); + return 1 - bal * bal; +} + +static int avl_remove_helper_found(struct avl_node **r, + struct avl_node **target) { + *target = *r; + + if ((*r)->left && (*r)->right) { + if ((*r)->balance < 0) { + int b = avl_remove_min(&((*r)->right), r); + avl_steal_node(*r, *target); + if (b == 0) + return 0; + (*r)->balance = 0; + return 1; + } else { + int b = avl_remove_max(&((*r)->left), r); + avl_steal_node(*r, *target); + if (b == 0) + return 0; + int bal = ((*r)->balance -= 1); + return 1 - (bal * bal); + } + } + + if ((*r)->left != NULL) + *r = (*r)->left; + else + *r = (*r)->right; + + avl_detach_node(*target); + return 1; +} + +struct remove_parameters { + avl_cmp_f cmp; + void *cookie; + enum { REMOVE_ANY, REMOVE_EXACT, } policy; +}; + +static int avl_remove_helper(struct avl_node **r, struct avl_node *v, + struct avl_node **target, const struct remove_parameters *params) { + if (*r == NULL) { + *target = NULL; + return 0; + } + + if (*r == v) // Implies equality according to `cmp` + return avl_remove_helper_found(r, target); + int c = params->cmp(v, *r, params->cookie); + if (c == 0 && params->policy == REMOVE_ANY) + return avl_remove_helper_found(r, target); + + if (c < 0) { + if (avl_remove_helper(&((*r)->left), v, target, params) == 0) + return 0; + (*r)->balance -= 1; + } else { + if (avl_remove_helper(&((*r)->right), v, target, params) == 0) + return 0; + (*r)->balance += 1; + } + int bal = rebalance(r); + return 1 - bal * bal; +} + +struct avl_node *avl_remove(struct avl *tree, struct avl_node *v) { + if (!tree || !v) + return NULL; + + struct avl_node *target = NULL; + const struct remove_parameters params = { + .cmp = tree->cmp, + .cookie = tree->cookie, + .policy = REMOVE_ANY, + }; + avl_remove_helper(&tree->root, v, &target, ¶ms); + return target; +} + +bool avl_remove_at(struct avl *tree, struct avl_node *v) { + if (!tree || !v) + return NULL; + + struct avl_node *target = NULL; + const struct remove_parameters params = { + .cmp = tree->cmp, + .cookie = tree->cookie, + .policy = REMOVE_EXACT, + }; + avl_remove_helper(&tree->root, v, &target, ¶ms); + return target == v; +} + +static struct avl_node *avl_find_helper(struct avl_node *r, + const struct avl_node *v, avl_cmp_f cmp, void *cookie) { + int c = 0; + + while (r && (c = cmp(v, r, cookie))) { + if (c < 0) + r = r->left; + else + r = r->right; + } + + return r; +} + +struct avl_node *avl_find(const struct avl *tree, const struct avl_node *v) { + if (!tree || !v) + return NULL; + + return avl_find_helper(tree->root, v, tree->cmp, tree->cookie); +} + +static struct avl_node *avl_lower_bound_helper(struct avl_node *r, + const struct avl_node *v, avl_cmp_f cmp, void *cookie) { + if (!r) + return NULL; + + int c = cmp(v, r, cookie); + + if (c <= 0) { + struct avl_node *lhs = avl_lower_bound_helper(r->left, v, cmp, cookie); + return (lhs ? lhs : r); + } + + return avl_lower_bound_helper(r->right, v, cmp, cookie); +} + +struct avl_node *avl_lower_bound(const struct avl *tree, + const struct avl_node *v) { + if (!tree || !v) + return NULL; + + return avl_lower_bound_helper(tree->root, v, tree->cmp, tree->cookie); +} + +static struct avl_node *avl_upper_bound_helper(struct avl_node *r, + const struct avl_node *v, avl_cmp_f cmp, void *cookie) { + if (!r) + return NULL; + + int c = cmp(v, r, cookie); + + if (c < 0) { + struct avl_node *lhs = avl_upper_bound_helper(r->left, v, cmp, cookie); + return (lhs ? lhs : r); + } + + return avl_upper_bound_helper(r->right, v, cmp, cookie); +} + +struct avl_node *avl_upper_bound(const struct avl *tree, + const struct avl_node *v) { + if (!tree || !v) + return NULL; + + return avl_upper_bound_helper(tree->root, v, tree->cmp, tree->cookie); +} + +bool avl_empty(const struct avl *tree) { + if (!tree || !tree->root) + return true; + return false; +} + +static size_t avl_size_helper(const struct avl_node *n) { + if (!n) + return 0; + + return 1 + avl_size_helper(n->left) + avl_size_helper(n->right); +} + +size_t avl_size(const struct avl *tree) { + if (!tree) + return 0; + + return avl_size_helper(tree->root); +} + +static size_t avl_height_helper(const struct avl_node *n) { + if (!n) + return 0; + + size_t lhs = avl_height_helper(n->left); + size_t rhs = avl_height_helper(n->right); + return 1 + (lhs > rhs ? lhs : rhs); +} + +size_t avl_height(const struct avl *tree) { + if (!tree) + return 0; + + return avl_height_helper(tree->root); +}