C binary search trees with malloc and free
Allocate tree nodes dynamically, traverse them in order, and release every node.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node *left;
struct Node *right;
} Node;
static Node *insert(Node *root, int value) {
if (root == NULL) {
Node *node = malloc(sizeof *node);
if (node == NULL) {
exit(1);
}
node->value = value;
node->left = NULL;
node->right = NULL;
return node;
}
if (value < root->value) {
root->left = insert(root->left, value);
} else if (value > root->value) {
root->right = insert(root->right, value);
}
return root;
}
static void in_order(const Node *root) {
if (root == NULL) {
return;
}
in_order(root->left);
printf(" %d", root->value);
in_order(root->right);
}
static int height(const Node *root) {
if (root == NULL) {
return 0;
}
int l = height(root->left);
int r = height(root->right);
return 1 + (l > r ? l : r);
}
static void free_tree(Node *root) {
if (root == NULL) {
return;
}
free_tree(root->left);
free_tree(root->right);
free(root);
}
int main(void) {
Node *root = NULL;
int values[] = {50, 30, 70, 20, 40, 60, 80, 40};
for (int i = 0; i < 8; i++) {
root = insert(root, values[i]);
}
printf("in order:");
in_order(root);
printf("\n");
printf("height: %d\n", height(root));
free_tree(root);
return 0;
}in order: 20 30 40 50 60 70 80 height: 3
Explanation
A tree node in C is a struct with a value and two pointers to child nodes, where NULL means no child. Nodes are allocated with malloc, so the program owns the memory and must release it with free. A recursive insert function walks down one path and attaches the new node at the first NULL pointer it reaches.
An in-order traversal visits the left subtree, the node, and then the right subtree, which gives the values of a binary search tree in sorted order. Freeing a tree must be done in post-order: free the children first, and then the node itself, or the pointers to the children are lost. A tool such as Valgrind or AddressSanitizer reports leaks and invalid frees.
Explore the dry run
#include <stdio.h>#include <stdlib.h> typedef struct Node { int value; struct Node *left; struct Node *right;} Node; static Node *insert(Node *root, int value) { if (root == NULL) { Node *node = malloc(sizeof *node); if (node == NULL) { exit(1); } node->value = value; node->left = NULL; node->right = NULL; return node; } if (value < root->value) { root->left = insert(root->left, value); } else if (value > root->value) { root->right = insert(root->right, value); } return root;} static void in_order(const Node *root) { if (root == NULL) { return; } in_order(root->left); printf(" %d", root->value); in_order(root->right);} static int height(const Node *root) { if (root == NULL) { return 0; } int l = height(root->left); int r = height(root->right); return 1 + (l > r ? l : r);} static void free_tree(Node *root) { if (root == NULL) { return; } free_tree(root->left); free_tree(root->right); free(root);} int main(void) { Node *root = NULL; int values[] = {50, 30, 70, 20, 40, 60, 80, 40}; for (int i = 0; i < 8; i++) { root = insert(root, values[i]); } printf("in order:"); in_order(root); printf("\n"); printf("height: %d\n", height(root)); free_tree(root); return 0;}Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| A new node is allocated on the heap when an empty spot is reached. | — | — | |
| Smaller values go left. Equal values, like the second 40, are ignored. | — | — | |
| Visit the whole left subtree first, so smaller values are printed before the node. | — | — | |
| Seven values in a balanced shape make a tree of height 3. | — | in order: 20 30 40 50 60 70 80 height: 3 | |
| Free the children before the node itself. | — | in order: 20 30 40 50 60 70 80 height: 3 |
Cost and common mistakes
Insert and search O(h) for height h, which is O(log n) if balanced and O(n) in the worst case.
- Forgetting free_tree leaks every node. Freeing the root first would lose the pointers to the children.
- Inserting sorted values makes a chain, and the recursion depth equals n. Self-balancing trees avoid that.
- malloc may return NULL. The example stops the program in that case.
Try it yourself
Which function visits nodes in the order 50 30 20 40 70 60 80?
Reveal answer and reasoning
A pre-order traversal: print the node first, then the left subtree, then the right subtree.