1
0
C_lib/trees/binary_search_tree/binary_search_tree.c
2021-11-07 14:47:00 +02:00

278 lines
9.1 KiB
C

/* This file contains the function implementations for binary search trees. */
/* Include statements. */
#include "../binary_tree/binary_tree.c"
#include "binary_search_tree.h"
/* This function initializes a new empty tree node, will return NULL on failure. */
tree_node_t* initialize_binary_search_tree_node() {
/* Delegate internally. */
return initialize_tree_node();
}
/* This function initializes a new tree node and attempts to store the given data within, will return NULL on failure. */
tree_node_t* initialize_binary_search_tree_node_store(void *data) {
/* Delegate internally. */
return initialize_tree_node_store(data);
}
/* This function is used to recursively fill an array into a BST. */
tree_node_t* initialize_binary_search_tree_recursive(void **array, int index_start, int index_end) {
/* Local variables. */
int middle_index;
tree_node_t *root;
/* Calculate the middle index. */
middle_index = index_start + index_end;
middle_index /= 2;
/* Allocate the node. */
root = (tree_node_t*) malloc(sizeof(tree_node_t));
if (root == NULL)
return NULL;
/* Set the value. */
root->data = array[middle_index];
/* Check if there is a need to recurse. */
if (index_start == index_end) {
/* Make a leaf and stop. */
root->right = NULL;
root->left = NULL;
return root;
}
/* Recursive case. */
root->right = initialize_binary_search_tree_recursive(array, middle_index, index_end);
root->left = initialize_binary_search_tree_recursive(array, index_start, middle_index);
/* Return the node. */
return root;
}
/* This function creates a BST for a sorted array. */
tree_node_t* initialize_binary_search_tree(void **array, int length) {
/* Sanity check. */
if (array == NULL || length == 0)
return NULL;
/* Delegate. */
return initialize_binary_search_tree_recursive(array, 0, length - 1);
}
/* This function links a node to the right, will overwrite links! Returns 0 on success and 1 on failure. */
int link_binary_search_tree_node_right(tree_node_t *parent, tree_node_t *child) {
/* Sanity check. */
if (parent == NULL || child == NULL)
return TREE_FAILURE_CODE;
/* Link and return success. */
parent->right = child;
return TREE_SUCCESS_CODE;
}
/* This function links a node to the left, will overwrite links! Returns 0 on success and 1 on failure. */
int link_binary_search_tree_node_left(tree_node_t *parent, tree_node_t *child) {
/* Sanity check. */
if (parent == NULL || child == NULL)
return TREE_FAILURE_CODE;
/* Link and return success. */
parent->left = child;
return TREE_SUCCESS_CODE;
}
/* TODO: rewrite iteratively? */
/* This function attempts to free a whole tree (recursively), while not touching the data. */
void free_binary_search_tree(tree_node_t *root) {
/* Stopping conditions. */
if (root == NULL)
return;
if (root->right == NULL && root->left == NULL) {
free(root);
return;
}
/* Recursive case. */
if (root->right != NULL) {
free_binary_search_tree(root->right);
root->right = NULL;
}
if (root->left != NULL) {
free_binary_search_tree(root->left);
root->left = NULL;
}
/* Free the current node. */
free(root);
}
/* TODO: rewrite iteratively? */
/* This function attempts to free a whole tree (recursively), while using free_function to free the data, if the latter is NULL - will use stdlib free. */
void free_binary_search_tree_data(tree_node_t *root, void (*free_function)(const void*)) {
/* Stopping conditions. */
if (root == NULL)
return;
if (root->right == NULL && root->left == NULL) {
if (free_function == NULL)
free(root->data);
else
free_function(root->data);
free(root);
return;
}
/* Recursive case. */
if (root->right != NULL) {
free_binary_search_tree_data(root->right, free_function);
root->right = NULL;
}
if (root->left != NULL) {
free_binary_search_tree_data(root->left, free_function);
root->left = NULL;
}
/* Free the current node. */
if (free_function == NULL)
free(root->data);
else
free_function(root->data);
free(root);
}
/* Note that the implementation disallows duplication! */
/* This function inserts data into the tree, will fail on invalid input. Comparison function should return 0 for equal, 1 for larger than and -1 for less than. */
int insert_node_into_binary_search_tree(tree_node_t *root, tree_node_t *to_insert, int (*comparison_function)(const void*, const void*)) {
/* Local variables. */
int comparison_result;
/* Sanity check. */
if (root == NULL || comparison_function == NULL)
return TREE_FAILURE_CODE;
/* Compare. */
comparison_result = (*comparison_function)(root->data, to_insert->data);
/* Check if the value is already there. */
if (comparison_result == TREE_COMPARISON_EQUAL_TO)
return TREE_SUCCESS_CODE;
/* Check if the root is smaller. */
if (comparison_result == TREE_COMPARISON_SMALLER_THAN) {
/* The root is smaller. */
if (root->right == NULL) {
/* Insert to the right. */
root->right = to_insert;
return TREE_SUCCESS_CODE;
}
/* Recurse. */
return insert_node_into_binary_search_tree(root->right, to_insert, comparison_function);
} else {
/* The root is larger. */
if (root->left == NULL) {
/* Insert to the left. */
root->right = to_insert;
return TREE_SUCCESS_CODE;
}
/* Recurse. */
return insert_node_into_binary_search_tree(root->left, to_insert, comparison_function);
}
/* Shouldn't ever reach here! */
return TREE_FAILURE_CODE;
}
/* This function searches for data in the tree. */
/* This function inserts data into the tree, will fail on invalid input. Comparison function should return 0 for equal, 1 for larger than and -1 for less than. */
tree_node_t* search_in_binary_search_tree(tree_node_t *root, void *data, int (*comparison_function)(const void*, const void*)) {
/* Local variables. */
int comparison_result;
/* Sanity check. */
if (root == NULL || comparison_function == NULL)
return NULL;
/* Check if we found the right node, and recurse accordingly. */
comparison_result = (*comparison_function)(root->data, data);
return ((comparison_result == 0) ? root : ((comparison_result == 1) ? search_in_binary_search_tree(root->left, data, comparison_function) : search_in_binary_search_tree(root->right, data, comparison_function)));
}
/* This function finds the tree's depth (at the deepest leaf). */
size_t find_binary_search_tree_depth(tree_node_t *root) {
/* Delegate. */
return find_tree_depth(root);
}
/* This function fills an array in a sorted manner with the values from a BST. */
/* Takes the tree's root, the array to fill, a pointer to the current fill index, and a pointer to the array's maximal length. */
/* Doesn't do much sanity checking on the input, returns defined constants on failure/success. */
int fill_array_binary_search_tree(tree_node_t *root, void **array, size_t *index, size_t length) {
/* Local variables. */
int flag_right, flag_left;
/* Initialize. */
flag_right = BINARY_SEARCH_TREE_SUCCESS_CODE;
flag_left = BINARY_SEARCH_TREE_SUCCESS_CODE;
/* Sanity check. */
if (root == NULL || array == NULL || *index < 0 || *index >= length)
return BINARY_SEARCH_TREE_FAILURE_CODE;
/* Check if we have reached a leaf. */
if (root->left == NULL && root->right == NULL) {
/* Insert into the array, and advance. */
array[*index] = root->data;
(*index)++;
return BINARY_SEARCH_TREE_SUCCESS_CODE;
}
/* Non-leaf node. */
/* Recurse to the left, add the current node's data, and then recurse to the right. */
/* Left. */
if (root->left != NULL)
flag_right = fill_array_binary_search_tree(root->left, array, index, length);
/* Current. */
array[*index] = root->data;
(*index)++;
/* Right. */
if (root->right != NULL)
flag_left = fill_array_binary_search_tree(root->right, array, index, length);
/* Done - Check if the flags are equal and successful, return accordingly (fail if one is false). */
return (flag_right == flag_left && flag_right == BINARY_SEARCH_TREE_SUCCESS_CODE) ? BINARY_SEARCH_TREE_SUCCESS_CODE : BINARY_SEARCH_TREE_FAILURE_CODE;
}
/* This function creates a sorted array, from a binary search tree. */
/* Returns a pointer to the first cell in the array on success, NULL on failure. */
void **sorted_array_from_binary_search_tree(tree_node_t *root, size_t *length) {
/* Sanity check. */
if (root == NULL || length == NULL)
return NULL;
/* Local variables. */
size_t height, array_size, index;
void **array;
/* Find the tree's height. */
height = find_binary_search_tree_depth(root);
/* Derive the array's (maximal) size based on said height (assumes a balanced and near-full tree). */
array_size = (size_t) (pow(2, height + 1) - 1);
/* Attempt to allocate the array. */
array = (void**) calloc(array_size, sizeof(void*));
/* Check for allocation failures. */
if (array == NULL)
return NULL;
/* Try to fill. */
index = 0;
/* Delegate. */
if (fill_array_binary_search_tree(root, array, &index, array_size) == BINARY_SEARCH_TREE_FAILURE_CODE) {
/* Failed! Free and return NULL. */
free(array);
return NULL;
}
/* Set the array's length. */
*length = ++index;
/* Resize the array if needed. */
if (index < array_size) {
/* Check for (non-fatal) failure. */
if (rallocarray(array, index, sizeof(void*)) == ENOMEM)
printf(BINARY_SEARCH_TREE_RESIZING_ARRAY_FAILURE_CODE);
}
/* Success. */
return array;
}