Data Structures

Binary Tree Data Structure

Hierarchical data structures with traversals, BST, AVL trees, and applications.

Tree Terminologies

🌳 Basic Terms

TermDefinition
RootTopmost node of the tree
ParentNode that has child nodes
ChildNode directly connected to another node when moving away from root
SiblingNodes with same parent
LeafNode with no children
Internal NodeNode with at least one child
DepthNumber of edges from root to node
HeightNumber of edges on longest path from node to leaf
LevelRoot at level 0, children at level 1, etc.
DegreeNumber of children of a node
        1          ← Root (Level 0, Depth 0)
       / \
      2   3        ← Level 1
     / \   \
    4   5   6      ← Level 2 (Leaves)
   
    Node 1: Parent of 2,3 | Degree 2 | Height 2
    Node 2: Parent of 4,5 | Child of 1 | Degree 2
    Node 4: Leaf | Child of 2 | Depth 2
                    

Binary Tree Properties

📐 Key Mathematical Properties

  1. Maximum nodes at level i: 2^i
  2. Maximum nodes in tree of height h: 2^(h+1) - 1
  3. Minimum nodes in tree of height h: h + 1
  4. For any non-empty binary tree: n0 = n2 + 1
    (n0 = leaf nodes, n2 = nodes with 2 children)
  5. Height of tree with n nodes: ⌊log₂(n)⌋ to n-1

Binary Tree Representations

1. Array Representation

For complete binary tree: Left child at 2i+1, Right child at 2i+2

Tree:        1
            / \
           2   3
          / \   \
         4   5   6

Array Index: 0  1  2  3  4  5
Array:     [1, 2, 3, 4, 5, 6]

Node at index i:
  - Left child: 2*i + 1
  - Right child: 2*i + 2
  - Parent: (i-1) / 2
                    
array_representation.c
#include <stdio.h>
#include <math.h>
#define MAX 100

void insert(int tree[], int* size, int data) {
    if (*size >= MAX) {
        printf("Tree is full!\n");
        return;
    }
    tree[*size] = data;
    (*size)++;
}

int leftChild(int tree[], int index, int size) {
    int left = 2 * index + 1;
    return (left < size) ? tree[left] : -1;
}

int rightChild(int tree[], int index, int size) {
    int right = 2 * index + 2;
    return (right < size) ? tree[right] : -1;
}

void display(int tree[], int size) {
    printf("Array representation: ");
    for (int i = 0; i < size; i++)
        printf("%d ", tree[i]);
    printf("\n");
}

int main() {
    int tree[MAX], size = 0;
    
    insert(tree, &size, 1);
    insert(tree, &size, 2);
    insert(tree, &size, 3);
    insert(tree, &size, 4);
    insert(tree, &size, 5);
    
    display(tree, size);
    
    printf("Left child of %d: %d\n", tree[0], leftChild(tree, 0, size));
    printf("Right child of %d: %d\n", tree[0], rightChild(tree, 0, size));
    
    return 0;
}

2. Linked Representation

linked_representation.c
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node* left;
    struct Node* right;
} Node;

Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

int main() {
    // Create tree:     1
    //                /   \
    //               2     3
    //              / \   /
    //             4   5 6
    
    Node* root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    root->right->left = createNode(6);
    
    printf("Root: %d\n", root->data);
    printf("Left child: %d\n", root->left->data);
    printf("Right child: %d\n", root->right->data);
    
    return 0;
}

Binary Tree Traversals

🔄 Types of Traversals

TraversalOrderUse Case
InorderLeft-Root-RightGives sorted order for BST
PreorderRoot-Left-RightCopy/serialize tree
PostorderLeft-Right-RootDelete tree, expression evaluation
        1
       / \
      2   3
     / \   \
    4   5   6

Inorder:   4 → 2 → 5 → 1 → 3 → 6    (Left-Root-Right)
Preorder:  1 → 2 → 4 → 5 → 3 → 6    (Root-Left-Right)
Postorder: 4 → 5 → 2 → 6 → 3 → 1    (Left-Right-Root)
                    
traversals.c
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node* left;
    struct Node* right;
} Node;

Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->left = newNode->right = NULL;
    return newNode;
}

// Preorder: Root -> Left -> Right
void preorder(Node* root) {
    if (root == NULL) return;
    printf("%d ", root->data);
    preorder(root->left);
    preorder(root->right);
}

// Inorder: Left -> Root -> Right
void inorder(Node* root) {
    if (root == NULL) return;
    inorder(root->left);
    printf("%d ", root->data);
    inorder(root->right);
}

// Postorder: Left -> Right -> Root
void postorder(Node* root) {
    if (root == NULL) return;
    postorder(root->left);
    postorder(root->right);
    printf("%d ", root->data);
}

int main() {
    Node* root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    root->right->right = createNode(6);
    
    printf("Preorder:   "); preorder(root); printf("\n");
    printf("Inorder:    "); inorder(root); printf("\n");
    printf("Postorder:  "); postorder(root); printf("\n");
    
    return 0;
}

Complexity

TraversalTimeSpace
Inorder/Preorder/PostorderO(n)O(h) recursion stack

Threaded Binary Trees

🔗 Concept

Threaded binary trees store pointers to inorder predecessor/successor in null child pointers to optimize traversal without recursion or stack.

  • Single Threaded: Store one thread (successor or predecessor)
  • Double Threaded: Store both predecessor and successor

Advantages:

  • Inorder traversal without stack (O(1) space)
  • Faster successor/predecessor access
  • Efficient for frequent traversal operations

Binary Search Tree (BST)

A BST is a binary tree where: Left subtree values < Root < Right subtree values

bst_complete.c
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node* left;
    struct Node* right;
} Node;

Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->left = newNode->right = NULL;
    return newNode;
}

// Insert into BST
Node* insert(Node* root, int data) {
    if (root == NULL) return createNode(data);
    if (data < root->data)
        root->left = insert(root->left, data);
    else if (data > root->data)
        root->right = insert(root->right, data);
    return root;
}

// Search in BST
Node* search(Node* root, int key) {
    if (root == NULL || root->data == key) return root;
    if (key < root->data) return search(root->left, key);
    return search(root->right, key);
}

// Find minimum
Node* findMin(Node* root) {
    while (root->left != NULL) root = root->left;
    return root;
}

// Find maximum
Node* findMax(Node* root) {
    while (root->right != NULL) root = root->right;
    return root;
}

// Delete from BST
Node* deleteNode(Node* root, int key) {
    if (root == NULL) return root;
    
    if (key < root->data)
        root->left = deleteNode(root->left, key);
    else if (key > root->data)
        root->right = deleteNode(root->right, key);
    else {
        // Node with one or no child
        if (root->left == NULL) {
            Node* temp = root->right;
            free(root);
            return temp;
        }
        else if (root->right == NULL) {
            Node* temp = root->left;
            free(root);
            return temp;
        }
        // Node with two children
        Node* temp = findMin(root->right);
        root->data = temp->data;
        root->right = deleteNode(root->right, temp->data);
    }
    return root;
}

void inorder(Node* root) {
    if (root == NULL) return;
    inorder(root->left);
    printf("%d ", root->data);
    inorder(root->right);
}

int main() {
    Node* root = NULL;
    int values[] = {50, 30, 70, 20, 40, 60, 80};
    int n = sizeof(values)/sizeof(values[0]);
    
    for (int i = 0; i < n; i++)
        root = insert(root, values[i]);
    
    printf("Inorder (sorted): "); inorder(root); printf("\n");
    
    if (search(root, 40)) printf("Found 40\n");
    
    printf("Min: %d, Max: %d\n", findMin(root)->data, findMax(root)->data);
    
    root = deleteNode(root, 30);
    printf("After deleting 30: "); inorder(root); printf("\n");
    
    return 0;
}
OperationAverageWorst (skewed)
SearchO(log n)O(n)
InsertO(log n)O(n)
DeleteO(log n)O(n)

AVL Trees (Self-Balancing BST)

⚖️ Balance Factor

Balance Factor = Height(Left Subtree) - Height(Right Subtree)

Valid values: -1, 0, 1 (tree is balanced)

🔁 Rotations

RotationConditionAction
LL (Left Rotation)BF > 1, left heavyRight rotate
RR (Right Rotation)BF < -1, right heavyLeft rotate
LR (Left-Right)BF > 1, right-heavy leftLeft rotate, then right rotate
RL (Right-Left)BF < -1, left-heavy rightRight rotate, then left rotate

Time Complexity: All operations O(log n) guaranteed

Expression Trees

Binary tree representation of arithmetic expressions.

Infix: (A + B) * (C - D)
Postfix: A B + C D - *

Expression Tree:
        *
       / \
      +   -
     / \  / \
    A  B C  D

Inorder: A + B * C - D (need parentheses)
Preorder: * + A B - C D (Polish notation)
Postorder: A B + C D - * (Reverse Polish notation)
                    

Tree Applications

📁 File Systems

Directory structures, folder hierarchies

🔍 Expression Evaluation

Compilers use expression trees

🗂️ Decision Trees

Machine learning, game trees

📊 Database Indexing

B-trees, B+ trees for efficient retrieval

🗜️ Compression

Huffman coding trees

🌐 Routing Tables

Network routing, DNS

Summary Table

TopicKey ConceptTime Complexity
Tree TraversalsInorder, Preorder, PostorderO(n)
BST SearchLeft < Root < RightO(log n) avg, O(n) worst
BST Insert/DeleteRecursive insertion/deletionO(log n) avg, O(n) worst
AVL TreeSelf-balancing, |BF| ≤ 1O(log n) guaranteed
Threaded TreeInorder without stackO(1) space traversal