From 203124ed6adc573e9b144715c47352cc57dd6ae9 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Wed, 25 Oct 2023 08:42:38 -0700 Subject: [PATCH] add vec_shrink --- docs/vector.md | 203 +++++++++++++++++++++++++++++++++++++++++++++++ include/vector.h | 4 + src/vector.c | 14 ++++ tests/tests.c | 5 ++ 4 files changed, 226 insertions(+) create mode 100644 docs/vector.md diff --git a/docs/vector.md b/docs/vector.md new file mode 100644 index 0000000..330959e --- /dev/null +++ b/docs/vector.md @@ -0,0 +1,203 @@ +# binarytree + +Binary tree with standard leaf operations + +## Usage + +Create the tree. The user is responsible for memory management of the `BinTree` struct. + +```c +BinTree *tree = malloc(sizeof(BinTree)); +``` + +After the tree is created, init it. The second argument on `bintree_init()` is an optional memory freeing function pointer +with signature `void (*destroy)(void *data)`. Use `free()` from the stdlib if you are creating the data with `malloc()`. +If allocation of your data is more complex, you can pass your own memory deallocation function as long as it fits the +signature. + +In this example, we are passing `NULL` because all memory will be stack allocated. + +```c +bintree_init(tree, NULL); +int root = 0; +int l1 = 1; +int l2 = 2; +int r1 = 12; +int r2 = 200; +``` + +Next lets insert our data into the tree. The insert functions signature is `bintree_ins_...(tree, parent, data)`. If you +are inserting data at the root of the tree, you may use either `bintree_ins_left()` or `bintree_ins_right()` as long as +`NULL` is passed as the parent argument. + +```c +bintree_ins_left(tree, NULL, &root); +bintree_ins_left(tree, tree->root, &l1); +bintree_ins_left(tree, tree->root->left, &l2); +bintree_ins_right(tree, tree->root->left, &r2); +bintree_ins_right(tree, tree->root, &r1); +bintree_ins_right(tree, tree->root->right, &r2); +bintree_ins_left(tree, tree->root->right, &l1); +``` + +We can use `bintree_debug_print(tree)` to print a graphical representation of the tree to `stdout` +```plaintext +└──0 + ├──1 + │ ├──2 + │ └──200 + └──12 + ├──1 + └──200 +``` + +To cleanup the tree, first destroy the nodes. If you passed a deallocation function, it will be called on +the data member of each node before the node itself is freed. `bintree_destroy()` does not free the tree itself, just the +nodes inside of it, hence we must also call `free()` on the tree. + +```c +bintree_destroy(tree); +free(tree); +tree = NULL; +``` + +Here is the entire example: + +```c +BinTree *tree = malloc(sizeof(BinTree)); +bintree_init(tree, NULL); + +int root = 0; +int l1 = 1; +int l2 = 2; +int r1 = 12; +int r2 = 200; + +bintree_ins_left(tree, NULL, &root); +bintree_ins_left(tree, tree->root, &l1); +bintree_ins_left(tree, tree->root->left, &l2); +bintree_ins_right(tree, tree->root->left, &r2); +bintree_ins_right(tree, tree->root, &r1); +bintree_ins_right(tree, tree->root->right, &r2); +bintree_ins_left(tree, tree->root->right, &l1); + +bintree_debug_print(tree); + +bintree_destroy(tree); +free(tree); +tree = NULL; +``` + +## Structs + +### BinTree + +Binary tree struct + +```c +typedef struct { + int size; + int (*compare)(const void *a, const void *b); + void (*destroy)(void *data); + struct BinTreeNode *root; +} BinTree; +``` + +Members: + +- `size`: How many nodes the tree contains +- `compare`: Comparison function between data in two nodes. Currently not used for anything +- `destroy`: Optional deallocation function for data inside a node. Typical usage is `NULL` for stack allocated data and `free()` for data created with `malloc()` +- `root`: The root node of the tree + +### BinTreeNode + +Node of the tree + +```c +typedef struct BinTreeNode { + void *data; + struct BinTreeNode *left; + struct BinTreeNode *right; +} BinTreeNode; +``` + +Members: +- `data`: void pointer to data the node contains +- `left`: left facing leaf of the node +- `right`: right facing leaf of the node + +## Functions + +### bintree_init + +Initialize the binary tree. User is responsible for freeing memory with `bintree_destroy()`. + +```c +void bintree_init(BinTree *tree, void (*destroy)(void *data)) +``` + +### bintree_destroy + +Destroys the nodes inside a tree and calls the deallaction function on the data if one was provided. Does not deallocate +the tree itself, that is left to the user. + +```c +void bintree_destroy(BinTree *tree) +``` + +### bintree_ins_left + +Creates a new node containing `data` and inserts it as the left child of `node`. + +```c +int bintree_ins_left(BinTree *tree, BinTreeNode *node, void *data) +``` + +### bintree_ins_right + +Creates a new node containing `data` and inserts it as the right child of `node`. + +```c +int bintree_ins_right(BinTree *tree, BinTreeNode *node, void *data) +``` + +### bintree_rem_left + +Removes and deallocates the left child node of `node`. Calls the deallocation function on the data if one was provided. + +```c +void bintree_rem_left(BinTree *tree, BinTreeNode *node) +``` + +### bintree_rem_right + +Removes and deallocates the right child node of `node`. Calls the deallocation function on the data if one was provided. + +```c +void bintree_rem_right(BinTree *tree, BinTreeNode *node) +``` + +### bintree_debug_print + +Prints a representation of the tree to stdout. Gets very messy with large trees. + +```c +void bintree_debug_print(BinTree *tree) +``` + +### bintree_is_eob + +Utility macro that checks if the node is the End Of Branch. + +```c +#define bintree_is_eob(node) ((node) == NULL) +``` + +### bintree_is_leaf + +Utility macro that checks if a node has children. + +```c +#define bintree_is_leaf(node) ((node)->left == NULL && (node)->right == NULL) +``` diff --git a/include/vector.h b/include/vector.h index 0f055a3..d9e892f 100644 --- a/include/vector.h +++ b/include/vector.h @@ -24,8 +24,12 @@ void *vec_safe_at(Vector *vec, size_t index); void *vec_remove(Vector *vec, size_t index); +int vec_shrink(Vector *vec); + #define vec_at(v, i) (v)->elements[(i)] #define vec_len(v) (v)->length +#define vec_cap(v) (v)->capacity + #endif // LIBFLINT_H_VECTOR diff --git a/src/vector.c b/src/vector.c index 4145556..3df195f 100644 --- a/src/vector.c +++ b/src/vector.c @@ -95,3 +95,17 @@ void *vec_remove(Vector *vec, size_t index) { vec->length -= 1; return r; } + +int vec_shrink(Vector *vec) { + if (vec_len(vec) == vec_cap(vec)) { + return 0; + } + + vec->capacity = vec_len(vec); + vec->elements = reallocf(vec, sizeof(void*) * vec_cap(vec)); + if (vec->elements == NULL) { + return -1; + } + + return 0; +} diff --git a/tests/tests.c b/tests/tests.c index 0dd9bc4..8730453 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -236,6 +236,11 @@ void test_vector() { t = (int*)vec_remove(v, 10); assert(t == NULL); + printf("\ncap before shrink: %zu\n", vec_cap(v)); + vec_shrink(v); + assert(vec_len(v) == vec_cap(v)); + printf("cap after shrink: %zu\n", vec_cap(v)); + vec_destroy(v); free(v); }