What is a Tree?
A tree is a non-linear, hierarchical data structure made of nodes connected by edges. Unlike arrays or linked lists (which are linear), a tree branches out โ one node can point to many children. Trees model real hierarchies: file systems, HTML DOM, organisation charts, and decision trees.
Key rules: a tree with N nodes has exactly Nโ1 edges. There is exactly one root node (no parent). Every non-root node has exactly one parent. There are no cycles.
Key Tree Terminology
Binary Tree Node Structure
In C, each node is a struct with three fields: the data value, a left pointer to the left child, and a right pointer to the right child. Both child pointers are NULL when there is no child. The entire tree is accessed through a single root pointer. All node creation uses malloc โ the tree lives on the heap.
The three classic depth-first traversals visit every node exactly once, differing only in when the current node's data is processed relative to its children: Inorder (Left โ Root โ Right), Preorder (Root โ Left โ Right), Postorder (Left โ Right โ Root). All three are implemented recursively โ the recursion naturally mirrors the tree's own recursive structure.
#include <stdio.h> #include <stdlib.h> /* โโ Node struct โโ */ typedef struct Node { int data; struct Node *left; struct Node *right; } Node; /* โโ Create a new heap node โโ */ Node* newNode(int val) { Node *n = malloc(sizeof(Node)); n->data = val; n->left = n->right = NULL; return n; } /* โโ INORDER : Left โ Root โ Right โโ */ void inorder(Node *root) { if (root == NULL) return; inorder(root->left); /* recurse left */ printf("%d ", root->data); /* visit node */ inorder(root->right); /* recurse right */ } /* โโ PREORDER : Root โ Left โ Right โโ */ void preorder(Node *root) { if (root == NULL) return; printf("%d ", root->data); preorder(root->left); preorder(root->right); } /* โโ POSTORDER: Left โ Right โ Root โโ */ void postorder(Node *root) { if (root == NULL) return; postorder(root->left); postorder(root->right); printf("%d ", root->data); } /* โโ Height: longest path root to leaf โโ */ int height(Node *root) { if (root == NULL) return 0; int lh = height(root->left); int rh = height(root->right); return 1 + (lh > rh ? lh : rh); } /* โโ Count all nodes โโ */ int countNodes(Node *root) { if (root == NULL) return 0; return 1 + countNodes(root->left) + countNodes(root->right); } /* โโ Count leaf nodes โโ */ int countLeaves(Node *root) { if (root == NULL) return 0; if (root->left == NULL && root->right == NULL) return 1; return countLeaves(root->left) + countLeaves(root->right); } /* โโ Free entire tree (postorder delete) โโ */ void freeTree(Node *root) { if (root == NULL) return; freeTree(root->left); freeTree(root->right); free(root); } int main() { /* Build the tree manually: 1 / \ 2 3 / \ / \ 4 5 6 7 */ Node *root = newNode(1); root->left = newNode(2); root->right = newNode(3); root->left->left = newNode(4); root->left->right = newNode(5); root->right->left = newNode(6); root->right->right = newNode(7); printf("=== Binary Tree ===\n\n"); printf("Inorder (L-N-R): "); inorder(root); printf("\n"); printf("Preorder (N-L-R): "); preorder(root); printf("\n"); printf("Postorder (L-R-N): "); postorder(root); printf("\n"); printf("\nHeight : %d\n", height(root)); printf("Total nodes : %d\n", countNodes(root)); printf("Leaf nodes : %d\n", countLeaves(root)); freeTree(root); printf("\nTree freed.\n"); return 0; }
=== Binary Tree === Inorder (L-N-R): 4 2 5 1 6 3 7 Preorder (N-L-R): 1 2 4 5 3 6 7 Postorder (L-R-N): 4 5 2 6 7 3 1 Height : 3 Total nodes : 7 Leaf nodes : 4 Tree freed.
if (root == NULL) return; as the base case, then recurse on root->left and root->right, and process root->data before, between, or after the two recursive calls to get Pre, In, or Postorder respectively. Change the order of those three lines and you change the traversal.freeTree calls itself on left and right children first, then frees the current node. If you freed the root first, you'd lose all pointers to children and leak the entire subtree. Postorder naturally handles children-before-parent โ which is exactly what safe deletion requires.The BST Property
A Binary Search Tree is a binary tree with one extra rule โ the BST property: for every node N, all values in the left subtree of N are less than N's value, and all values in the right subtree are greater than N's value. This ordering is maintained for every node in the tree, not just the root.
Because of this property, searching for a value takes O(h) time where h is the height โ O(log n) for a balanced BST, O(n) worst case (a skewed tree that behaves like a linked list). Insert and delete are also O(h). Inorder traversal of a BST always yields the values in sorted ascending order.
#include <stdio.h> #include <stdlib.h> typedef struct BSTNode { int data; struct BSTNode *left; struct BSTNode *right; } BSTNode; /* โโ Create node โโ */ BSTNode* newNode(int val) { BSTNode *n = malloc(sizeof(BSTNode)); n->data = val; n->left = n->right = NULL; return n; } /* โโ INSERT: returns updated root โโ */ BSTNode* insert(BSTNode *root, int val) { if (root == NULL) return newNode(val); /* found the slot */ if (val < root->data) root->left = insert(root->left, val); /* go left */ else if (val > root->data) root->right = insert(root->right, val); /* go right */ /* equal: no duplicate inserted */ return root; } /* โโ SEARCH: returns node or NULL โโ */ BSTNode* search(BSTNode *root, int val) { if (root == NULL || root->data == val) return root; if (val < root->data) return search(root->left, val); else return search(root->right, val); } /* โโ MIN / MAX โโ */ BSTNode* minNode(BSTNode *root) { while (root && root->left) root = root->left; return root; } BSTNode* maxNode(BSTNode *root) { while (root && root->right) root = root->right; return root; } /* โโ HEIGHT โโ */ int height(BSTNode *root) { if (!root) return 0; int lh = height(root->left); int rh = height(root->right); return 1 + (lh > rh ? lh : rh); } /* โโ DELETE โโ Three cases: 1. Node has no children โ just remove it 2. Node has one child โ replace with that child 3. Node has two children โ replace with inorder successor (smallest in right subtree), then delete successor */ BSTNode* deleteNode(BSTNode *root, int val) { if (root == NULL) return NULL; if (val < root->data) { root->left = deleteNode(root->left, val); } else if (val > root->data) { root->right = deleteNode(root->right, val); } else { /* Found the node to delete */ if (root->left == NULL) { /* case 1 & 2 */ BSTNode *tmp = root->right; free(root); return tmp; } if (root->right == NULL) { /* case 2 */ BSTNode *tmp = root->left; free(root); return tmp; } /* case 3: two children */ BSTNode *succ = minNode(root->right); root->data = succ->data; root->right = deleteNode(root->right, succ->data); } return root; } /* โโ Inorder: sorted output โโ */ void inorder(BSTNode *root) { if (!root) return; inorder(root->left); printf("%d ", root->data); inorder(root->right); } /* โโ Pretty print tree sideways โโ */ void printTree(BSTNode *root, int space) { if (!root) return; space += 5; printTree(root->right, space); printf("\n%*s%d\n", space, "", root->data); printTree(root->left, space); } int main() { BSTNode *root = NULL; printf("=== Binary Search Tree ===\n"); /* Build BST */ int vals[] = { 50, 30, 70, 20, 40, 60, 80, 45 }; for (int i = 0; i < 8; i++) root = insert(root, vals[i]); printf("\nTree (rotated 90ยฐ, right branch on top):"); printTree(root, 0); printf("\n\nInorder (sorted): "); inorder(root); printf("\nHeight : %d", height(root)); printf("\nMin : %d", minNode(root)->data); printf("\nMax : %d\n", maxNode(root)->data); /* Search */ printf("\n--- Search ---\n"); int targets[] = { 40, 99 }; for (int i = 0; i < 2; i++) { BSTNode *res = search(root, targets[i]); printf(" search(%d): %s\n", targets[i], res ? "FOUND" : "NOT FOUND"); } /* Delete */ printf("\n--- Delete 30 (node with two children) ---\n"); root = deleteNode(root, 30); printf("Inorder after delete: "); inorder(root); printf("\n"); printf("\n--- Delete 20 (leaf node) ---\n"); root = deleteNode(root, 20); printf("Inorder after delete: "); inorder(root); printf("\n"); return 0; }
=== Binary Search Tree ===
Tree (rotated 90ยฐ, right branch on top):
80
70
60
50
45
40
30
20
Inorder (sorted): 20 30 40 45 50 60 70 80
Height : 4
Min : 20
Max : 80
--- Search ---
search(40): FOUND
search(99): NOT FOUND
--- Delete 30 (node with two children) ---
Inorder after delete: 20 40 45 50 60 70 80
--- Delete 20 (leaf node) ---
Inorder after delete: 40 45 50 60 70 80
- Tree basics โ N nodes, Nโ1 edges. One root, one parent per non-root. Root has depth 0. Height = longest root-to-leaf path. Leaf = node with no children. Degree = number of children.
- Binary Tree node โ
structwithint data,Node *left,Node *right. Create withmalloc. Access whole tree via a single root pointer. Free with postorder deletion. - Inorder (LNR) = sorted output on BST. Preorder (NLR) = copy/serialize a tree. Postorder (LRN) = safe tree deletion. All three: base case
if (root==NULL) return, then recurse left+right, process data before/between/after. - BST property โ left subtree values < node < right subtree values, at every node. Insert: compare at each node, go left if smaller, right if larger, insert at first NULL. Inorder traversal gives sorted order.
- BST Search โ O(h). Compare target with node: equal โ found, smaller โ go left, larger โ go right. Returns the node pointer or NULL.
- BST Delete โ three cases: (1) leaf โ free, parent = NULL; (2) one child โ bypass, link parent to child; (3) two children โ copy inorder successor value (min of right subtree), delete successor recursively.
- BST worst case โ inserting sorted data makes a skewed tree (linked list), O(n) all operations. Fix with self-balancing trees (AVL, Red-Black). Balanced BST: height O(log n), all ops O(log n).