add vec_shrink

This commit is contained in:
Evan Burkey 2023-10-25 08:42:38 -07:00
parent ea2b8900dd
commit 203124ed6a
4 changed files with 226 additions and 0 deletions

203
docs/vector.md Normal file
View File

@ -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)
```

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}