278 lines
9.1 KiB
C
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;
|
|
}
|