I'm a novice C programmer (moving onto C++ soon) and I've tried to implement a basic (search,insertion,deletion) generic unbalanced BST whilst adhering to a few OO design principles. I'm looking for some feedback and advice from seasoned C programmers on my code and style.
tree.h
#ifndef TREE_H #define TREE_H struct btree_node { struct btree_node *left; struct btree_node *right; void *item; }; static void btree_free_node(struct btree_node *node); static struct btree_node* find_min_node(struct btree_node *node); static struct btree_node* find_max_node(struct btree_node *node); int btree_search(struct btree_node *root, void *item, int (*comp)(const void*,const void*)); int btree_insert(struct btree_node **root, void *item, unsigned int size, int (*comp)(const void*,const void*)); struct btree_node* btree_delete_node(struct btree_node *root, void *item, unsigned int size, int (*compare_node)(const void*,const void*)); void btree_print(struct btree_node *root, void (*print)(const void *)); void btree_free(struct btree_node *root); #endif
tree.c
#include "tree.h" #include <stdlib.h> #include <stdio.h> #include <string.h> int btree_insert(struct btree_node **root, void *item, unsigned int size, int (*compare_node)(const void*,const void*)) { // Insert the root if (*root == NULL) { *root = malloc(sizeof(struct btree_node)); if (!(*root)) { fprintf(stderr,"malloc() fail\n"); return 0; } (*root)->left = (*root)->right = NULL; (*root)->item = malloc(size); if (!((*root)->item)) { fprintf(stderr,"malloc() fail\n"); free(*root); return 0; } memcpy((*root)->item,item,size); } else { if (compare_node((*root)->item,item) > 0) { //Insert left btree_insert(&(*root)->left,item,size,compare_node); } else { //Insert right btree_insert(&(*root)->right,item,size,compare_node); } } return 1; } static void btree_free_node(struct btree_node *node) { free(node->item); free(node); } static struct btree_node* find_min_node(struct btree_node *node) { node = node->right; while (node) node = node->left; return node; } static struct btree_node* find_max_node(struct btree_node *node) { node = node->left; while (node) node = node->right; return node; } struct btree_node* btree_delete_node(struct btree_node *root, void *item, unsigned int size, int (*compare_node)(const void*,const void*)) { if (root == NULL) return root; else if (compare_node(item,root->item) < 0) root->left = btree_delete_node(root->left,item,size,compare_node); else if (compare_node(item,root->item) > 0) root->right = btree_delete_node(root->right,item,size,compare_node); else { // 1. Deleting a node with two children if ( root->left && root->right ) { struct btree_node *min_node = find_min_node(root); if (!min_node) { min_node = find_max_node(root); } memcpy(root->item,min_node->item,size); root->right = btree_delete_node(root->right,min_node->item,size,compare_node); } else if (root->left) { // 2. Deleting a node with one child (left) struct btree_node *node_delete = root; root = root->left; btree_free_node(node_delete); } else if (root->right) { // 2. Deleting a node with one child (right) struct btree_node *node_delete = root; root = root->right; btree_free_node(node_delete); } else { // 3. Deleting a leaf node btree_free_node(root); root = NULL; } } return root; } void btree_print(struct btree_node *root, void (*print_node)(const void *)) { if (root) { print_node(root->item); btree_print(root->left,print_node); btree_print(root->right,print_node); } } void btree_free(struct btree_node *root) { if (root) { free(root->item); btree_free(root->left); btree_free(root->right); free(root); } } int btree_search(struct btree_node *root, void *item, int (*compare_node)(const void*,const void*)) { if (root == NULL) return 0; else if (compare_node(item,root->item) > 0) return btree_search(root->right, item, compare_node); else if (compare_node(item,root->item) < 0) return btree_search(root->left, item, compare_node); else return 1; }
test.c
#include <stdio.h> #include "tree.h" void print_node(const void *node) { printf("%d\n",*(int*)node); } int compare_node(const void *a, const void *b) { return *(int*)a - *(int*)b; } int main() { struct btree_node *root = NULL; for (int i=0; i<10; i++) { btree_insert(&root,&i,sizeof(int),compare_node); } int a = 6; printf("found %d ? %d\n",a,btree_search(root, &a, compare_node)); a = 100; printf("found %d ? %d\n",a,btree_search(root, &a, compare_node)); a = 6; btree_delete_node(root, &a, sizeof(int),compare_node); printf("found %d ? %d\n",a,btree_search(root, &a, compare_node)); btree_print(root,print_node); btree_free(root); return 0; }
To compile and test
gcc test.c tree.c -o test.o -O3
./test.o
found 6 ? 1 found 100 ? 0 found 6 ? 0 0 1 2 3 4 5 7 8 9
Memory leak check with Valgrind
valgrind --leak-check=full --show-leak-kinds=all ./test.o
==91414== HEAP SUMMARY: ==91414== in use at exit: 0 bytes in 0 blocks ==91414== total heap usage: 21 allocs, 21 frees, 1,304 bytes allocated ==91414== ==91414== All heap blocks were freed -- no leaks are possible