Binary Tree with working insertions, no duplicates

This commit is contained in:
VegOwOtenks 2024-07-01 16:31:45 +02:00
parent 30f288e5d7
commit fe63781996
6 changed files with 464 additions and 0 deletions

288
src/BinaryTree/BinaryTree.c Normal file
View file

@ -0,0 +1,288 @@
#include "BinaryTree.h"
#include <iso646.h>
#include <stdlib.h>
enum BinaryTreeNodePosition {
BINARYTREENODEPOSITION_SELF,
BINARYTREENODEPOSITION_LEFT,
BINARYTREENODEPOSITION_RIGHT,
};
typedef struct BinaryTreeWalker_s {
BinaryTreeNode* node;
enum BinaryTreeNodePosition position;
} BinaryTreeWalker;
static size_t _TreeNodeSize(BinaryTree* tree) {
return sizeof(BinaryTreeNode) + tree->value_size;
}
static inline size_t _DepthOf(BinaryTreeNode* node)
{
return node == NULL ? 0 : node->depth;
}
static BinaryTreeNode* _CreateNode(BinaryTree* tree, void* value) {
BinaryTreeNode* node = Allocator_Allocate(tree->allocator, _TreeNodeSize(tree));
if (node != NULL) {
memcpy(node->value, value, tree->value_size);
node->depth = 1;
node->left = NULL;
node-> right = NULL;
}
return node;
}
int BinaryTree_Create(BinaryTree* target, BinaryTreeComparator compare, void* xdata, size_t value_size, allocator_t* allocator) {
target->root = NULL;
target->compare = compare;
target->xdata = xdata;
target->allocator = allocator;
target->value_size = value_size;
int array_code = DynamicArray_Create(&target->walker_stack, sizeof(BinaryTreeWalker), 8, allocator);
if (array_code) return array_code;
return EXIT_SUCCESS;
}
static void _UpdateDepth(BinaryTreeNode* node) {
size_t depth_left = 0, depth_right = 0;
if (node->left != NULL) {
depth_left = node->left->depth;
}
if (node->right != NULL) {
depth_right = node->right->depth;
}
if (depth_left > depth_right) {
node->depth = depth_left;
} else {
node->depth = depth_right;
}
node->depth += 1;
}
static void _UpdateDepths(DynamicArray* parents) {
for (size_t i = parents->reserved - 1; i != SIZE_MAX; i--) {
BinaryTreeWalker* walker = DynamicArray_GetPointer(parents, i);
_UpdateDepth(walker->node);
}
}
int _Insert(BinaryTree* tree, BinaryTreeNode* node) {
BinaryTreeNode** parent = &tree->root;
tree->walker_stack.reserved = 0;
if (*parent == NULL) {
*parent = node;
}
while ((*parent) != node) {
BinaryTreeWalker* walker;
DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &walker);
// Can't be NULL because the size was just increased
walker->node = *parent;
int comparison = tree->compare(node->value, (*parent)->value, tree->xdata);
if (comparison == 0) {
// equal
return EXIT_FAILURE;
} else if (comparison < 0) {
// less than
if ((*parent)->left == NULL) {
(*parent)->left = node;
}
parent = &(*parent)->left;
walker->position = BINARYTREENODEPOSITION_LEFT;
} else {
// greater than
if ((*parent)->right == NULL) {
(*parent)->right = node;
}
parent = &(*parent)->right;
walker->position = BINARYTREENODEPOSITION_RIGHT;
}
}
_UpdateDepths(&tree->walker_stack);
_UpdateDepth(node);
return EXIT_SUCCESS;
}
int _GrowParentStack(BinaryTree* tree)
{
size_t depth = _DepthOf(tree->root);
if (tree->walker_stack.capacity < depth + 1) {
return DynamicArray_Resize(&tree->walker_stack, tree->walker_stack.capacity * 2);
}
return EXIT_SUCCESS;
}
// right is heavier, shift it to the left
static void _RotateLeft(BinaryTreeNode** node)
{
BinaryTreeNode* right = (*node)->right;
(*node)->right = right->left;
right->left = *node;
*node = right;
}
// left is heavier, shift it to the right
static void _RotateRight(BinaryTreeNode** node)
{
BinaryTreeNode* left = (*node)->left;
(*node)->left = left->right;
left->right = *node;
*node = left;
}
static void _RebalanceNode(BinaryTreeNode** node)
{
size_t left_depth = _DepthOf(node[0]->left);
size_t right_depth = _DepthOf(node[0]->right);
if (left_depth < right_depth && right_depth - left_depth > 1) {
_RotateLeft(node);
_UpdateDepth((*node)->left); // old node
_UpdateDepth(*node); // rotated node
} else if (right_depth < left_depth && left_depth - right_depth > 1) {
_RotateRight(node);
_UpdateDepth((*node)->right); // old node
_UpdateDepth(*node); // rotated node
}
}
static void _RebalanceRoot(BinaryTree* tree)
{
_RebalanceNode(&tree->root);
}
static void _Rebalance(BinaryTree* tree)
{
for (size_t i = tree->walker_stack.reserved - 1; i + 1 != 0; i--) {
BinaryTreeWalker* walker = DynamicArray_GetPointer(
&tree->walker_stack,
i
);
BinaryTreeNode** nodep = NULL;
switch (walker->position) {
case BINARYTREENODEPOSITION_LEFT:
nodep = &walker->node->left;
break;
case BINARYTREENODEPOSITION_RIGHT:
nodep = &walker->node->right;
break;
case BINARYTREENODEPOSITION_SELF:
// not gonna happen
break;
}
_RebalanceNode(nodep);
}
_RebalanceRoot(tree);
}
static int _BinaryTree_DestroyNode(BinaryTree* tree, BinaryTreeNode* node)
{
Allocator_Free(tree->allocator, node, sizeof(*node) + tree->value_size);
return EXIT_SUCCESS;
}
int BinaryTree_Insert(BinaryTree* tree, void* value) {
if (tree == NULL) return EDESTADDRREQ;
if (value == NULL) return EINVAL;
if (_GrowParentStack(tree)) {
return ENOMEM;
}
BinaryTreeNode* new_node = _CreateNode(tree, value);
if (new_node == NULL) return ENOMEM;
if (_Insert(tree, new_node)) {
_BinaryTree_DestroyNode(tree, new_node);
return EXIT_FAILURE;
}
_Rebalance(tree);
return EXIT_SUCCESS;
}
int BinaryTree_ForEachPostOrder(BinaryTree* tree, BinaryTreeNodeFunction action, void* context)
{
tree->walker_stack.reserved = 0;
{
BinaryTreeWalker* root_walker;
DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &root_walker);
root_walker->node = tree->root;
root_walker->position = BINARYTREENODEPOSITION_LEFT;
}
while (tree->walker_stack.reserved != 0) {
BinaryTreeWalker* current = DynamicArray_GetPointer(&tree->walker_stack, tree->walker_stack.reserved - 1);
if (current->node == NULL) {
tree->walker_stack.reserved--; // remove last item
continue;
}
BinaryTreeNode* append = NULL;
switch (current->position) {
case BINARYTREENODEPOSITION_SELF:
{
int error = action(context, current->node);
if (error) return ECANCELED;
tree->walker_stack.reserved--;
break;
}
case BINARYTREENODEPOSITION_LEFT:
current->position = BINARYTREENODEPOSITION_RIGHT;
append = current->node->left;
break;
case BINARYTREENODEPOSITION_RIGHT:
current->position = BINARYTREENODEPOSITION_SELF;
append = current->node->right;
break;
}
if (append != NULL) {
BinaryTreeWalker* new_walker;
DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &new_walker);
new_walker->node = append;
new_walker->position = BINARYTREENODEPOSITION_LEFT;
}
}
return EXIT_SUCCESS;
}
void BinaryTree_Destroy(BinaryTree* tree)
{
BinaryTree_ForEachPostOrder(tree, (BinaryTreeNodeFunction) _BinaryTree_DestroyNode, tree);
DynamicArray_Destroy(&tree->walker_stack);
return;
}

View file

@ -0,0 +1,40 @@
#ifndef UTILITIEC_BINARYTREE_H
#define UTILITIEC_BINARYTREE_H
#include "../allocator-interface/allocator-interface.h"
#include "../dynamicarray/dynamicarray.h"
typedef struct BinaryTreeNode_s {
struct BinaryTreeNode_s* left;
struct BinaryTreeNode_s* right;
size_t depth;
char value[];
} BinaryTreeNode;
typedef int (*BinaryTreeComparator) (void* this, void* other, void* xdata);
typedef struct BinaryTree_s {
BinaryTreeNode* root;
DynamicArray walker_stack;
size_t value_size;
BinaryTreeComparator compare;
void* xdata;
allocator_t* allocator;
} BinaryTree;
int BinaryTree_Create(BinaryTree* target, BinaryTreeComparator compare, void* xdata, size_t value_size, allocator_t* allocator);
void BinaryTree_Destroy(BinaryTree* tree);
int BinaryTree_Insert(BinaryTree* tree, void* value);
int BinaryTree_Remove(BinaryTree* tree, void* value);
typedef int (*BinaryTreeNodeFunction) (void* context, BinaryTreeNode* node);
int BinaryTree_ForEachPostOrder(BinaryTree* tree, BinaryTreeNodeFunction action, void* context);
#endif

View file

@ -0,0 +1 @@
add_library(BinaryTree STATIC BinaryTree.c)

View file

@ -3,6 +3,7 @@ add_subdirectory(./allocator-interface)
add_subdirectory(./FixedBuffer)
add_subdirectory(./dynamicbuffer)
add_subdirectory(./FreeList)
add_subdirectory(./BinaryTree)
add_subdirectory(./hashmap)
add_subdirectory(./QuadTree)
add_subdirectory(./threading)

126
tests/BinaryTree.test.c Normal file
View file

@ -0,0 +1,126 @@
#include <assert.h>
#include <stdlib.h>
#include "../src/BinaryTree/BinaryTree.h"
int doublecomparator(double* this, double* other, void* xdata)
{
assert(xdata == (void*) 0xDEADBEEF);
if (*this > *other) {
return 1;
} else if (*this < *other) {
return -1;
}
return 0;
}
void testLifetime(void)
{
allocator_t allocator;
Allocator_CreateSystemAllocator(&allocator);
BinaryTree tree;
assert(EXIT_SUCCESS == BinaryTree_Create(
&tree,
(BinaryTreeComparator) doublecomparator,
(void*) 0xDEADBEEF,
sizeof(double),
&allocator)
);
BinaryTree_Destroy(&tree);
assert(allocator.reserved == 0);
return;
}
void testInsert(void)
{
allocator_t allocator;
Allocator_CreateSystemAllocator(&allocator);
BinaryTree tree;
assert(EXIT_SUCCESS == BinaryTree_Create(
&tree,
(BinaryTreeComparator) doublecomparator,
(void*) 0xDEADBEEF,
sizeof(double),
&allocator)
);
double value = 5;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
assert(tree.root != NULL);
value = 3;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 2;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 4;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 7;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 6;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 8;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
BinaryTree_Destroy(&tree);
assert(allocator.reserved == 0);
return;
}
void testEqual(void)
{
allocator_t allocator;
Allocator_CreateSystemAllocator(&allocator);
BinaryTree tree;
assert(EXIT_SUCCESS == BinaryTree_Create(
&tree,
(BinaryTreeComparator) doublecomparator,
(void*) 0xDEADBEEF,
sizeof(double),
&allocator)
);
double value = 5;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
assert(tree.root != NULL);
value = 3;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 2;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 4;
assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value));
value = 5;
assert(EXIT_FAILURE == BinaryTree_Insert(&tree, &value));
BinaryTree_Destroy(&tree);
assert(allocator.reserved == 0);
return;
}
int main()
{
testLifetime();
testInsert();
testEqual();
return 0;
}

View file

@ -36,3 +36,11 @@ target_link_libraries(regex-test
StringView
utf8
)
add_executable(BinaryTree-test BinaryTree.test.c)
target_link_libraries(BinaryTree-test
BinaryTree
dynamicarray
pointers
allocator-interface
)