Binary Tree with working insertions, no duplicates
This commit is contained in:
parent
30f288e5d7
commit
fe63781996
6 changed files with 464 additions and 0 deletions
288
src/BinaryTree/BinaryTree.c
Normal file
288
src/BinaryTree/BinaryTree.c
Normal 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;
|
||||
}
|
40
src/BinaryTree/BinaryTree.h
Normal file
40
src/BinaryTree/BinaryTree.h
Normal 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
|
1
src/BinaryTree/CMakeLists.txt
Normal file
1
src/BinaryTree/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
|||
add_library(BinaryTree STATIC BinaryTree.c)
|
|
@ -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
126
tests/BinaryTree.test.c
Normal 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;
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue