From e88bfadf2c613602ff56d7827f2c1e3077e43818 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 19 Jan 2023 22:45:22 +0100 Subject: [PATCH] feat(tree): initially working binary tree --- Makefile | 12 ++-- src/tree/node.c | 125 +++++++++++++++++++++++++++++++---- src/tree/node.h | 11 ++- test/tree/test_binary_tree.c | 21 ++++++ 4 files changed, 146 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 65ac6f4..4137be1 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,8 @@ SRC_DIR ?= src TEST_DIR ?= test INC_DIRS ?= include +LIB := $(BUILD_DIR)/$(LIB_FILENAME) + SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find $(INC_DIRS) -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' @@ -34,8 +36,8 @@ all: vieter # =====COMPILATION===== .PHONY: vieter -vieter: $(BUILD_DIR)/$(LIB_FILENAME) -$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) +vieter: $(LIB) +$(LIB): $(OBJS) ar -rcs $@ $(OBJS) $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c @@ -55,10 +57,8 @@ test-mem: build-test .PHONY: build-test build-test: $(BINS_TEST) -# For simplicity, we link every object file to each of the test files. This -# might be changed later if this starts to become too slow. -$(BINS_TEST): %: %.c.o $(OBJS) - $(CC) $^ -o $@ +$(BINS_TEST): %: %.c.o $(LIB) + $(CC) -L$(BUILD_DIR) -lvieter $^ -o $@ # Each test includes the test directory, which contains the acutest header file $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c diff --git a/src/tree/node.c b/src/tree/node.c index 935a56d..131e932 100644 --- a/src/tree/node.c +++ b/src/tree/node.c @@ -4,9 +4,35 @@ vieter_tree_node *vieter_tree_node_init() { return calloc(1, sizeof(vieter_tree_node)); } -vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, +void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key, + vieter_tree_node *child) { + if (parent == NULL) { + return; + } + + if (key < parent->key) { + parent->left = child; + } else { + parent->right = child; + } + + if (child != NULL) { + child->parent = parent; + } +} + +void vieter_tree_node_replace(vieter_tree_node *to_replace, + vieter_tree_node *replacement) { + to_replace->key = replacement->key; + to_replace->data = replacement->data; + to_replace->left = replacement->left; + to_replace->right = replacement->right; +} + +vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, void *data) { - vieter_tree_node *parent = node; + vieter_tree_node *node = root; + vieter_tree_node *parent = root; while (node != NULL) { if (node->key == key) { @@ -15,7 +41,7 @@ vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, parent = node; - if (key < node->key) { + if (key < parent->key) { node = parent->left; } else { node = parent->right; @@ -25,22 +51,20 @@ vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, vieter_tree_node *new_node = vieter_tree_node_init(); new_node->key = key; new_node->data = data; - new_node->parent = parent; - if (key < node->key) { - parent->left = new_node; - } else { - parent->right = new_node; - } + vieter_tree_node_add_child(parent, key, new_node); return vieter_tree_ok; } -vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, - uint64_t key) { +vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out, + vieter_tree_node *root, + uint64_t key) { + vieter_tree_node *node = root; + while (node != NULL) { if (node->key == key) { - *out = node->data; + *out = node; return vieter_tree_ok; } @@ -55,7 +79,80 @@ vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, return vieter_tree_not_present; } -vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *node, +vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, uint64_t key) { - return vieter_tree_not_present; + vieter_tree_node *target; + vieter_tree_error res = vieter_tree_node_search_node(&target, root, key); + + if (res != vieter_tree_ok) { + return res; + } + + *out = target->data; + + return vieter_tree_ok; +} + +vieter_tree_node *vieter_tree_node_successor(vieter_tree_node *node) { + if ((node->left != NULL) ^ (node->right != NULL)) { + return node->left != NULL ? node->left : node->right; + } + + node = node->right; + + while (node->left != NULL) { + node = node->left; + } + + return node; +} + +vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, + uint64_t key) { + vieter_tree_node *target; + vieter_tree_error res = vieter_tree_node_search_node(&target, root, key); + + if (res != vieter_tree_ok) { + return res; + } + + *out = target->data; + + if (target->left == NULL && target->right == NULL) { + vieter_tree_node_add_child(target->parent, target->key, NULL); + + free(target); + } else if ((target->left == NULL) ^ (target->right == NULL)) { + vieter_tree_node *child = + target->left != NULL ? target->left : target->right; + + if (target->parent != NULL) { + vieter_tree_node_add_child(target->parent, child->key, child); + free(target); + } else { + vieter_tree_node_replace(target, child); + free(child); + } + + } else { + vieter_tree_node *replacement = target->right; + + while (replacement->left != NULL) { + replacement = replacement->left; + } + + // We use replacement->key here because the right child can be NULL, so + // reading its key isn't safe. Using replacement->key however, the child + // will still get placed into the right location because of how binary + // trees work. + vieter_tree_node_add_child(replacement->parent, replacement->key, + replacement->right); + + target->key = replacement->key; + target->data = replacement->data; + + free(replacement); + } + + return vieter_tree_ok; } diff --git a/src/tree/node.h b/src/tree/node.h index f22a965..68da70e 100644 --- a/src/tree/node.h +++ b/src/tree/node.h @@ -14,10 +14,15 @@ typedef struct vieter_tree_node { vieter_tree_node *vieter_tree_node_init(); -vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, void *data); +vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, void *data); -vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, uint64_t key); +vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out, vieter_tree_node *root, uint64_t key); +vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, uint64_t key); -vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *node, uint64_t key); +vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, uint64_t key); + +vieter_tree_node *vieter_tree_node_successor(vieter_tree_node *node); + +void vieter_tree_node_replace(vieter_tree_node *to_replace, vieter_tree_node *replacement); #endif diff --git a/test/tree/test_binary_tree.c b/test/tree/test_binary_tree.c index fd6bb1b..4b1ceba 100644 --- a/test/tree/test_binary_tree.c +++ b/test/tree/test_binary_tree.c @@ -17,16 +17,37 @@ void test_insert() { for (uint64_t i = 0; i < 25; i++) { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + TEST_SIZE(tree, i + 1); } void *out; for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); } } +void test_remove() { + vieter_tree *tree = vieter_tree_init(); + + for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + } + + void *out; + + for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); + TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok); + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present); + } +} + TEST_LIST = { + {"test_init", test_init}, + {"test_insert", test_insert}, + {"test_remove", test_remove}, {NULL, NULL} };