Data Structures & Algorithms — Exam Programs in C (Unit I – Unit X)

All programs are complete, standalone and compile with: gcc program.c -o program (add -lm only where noted)

Every program below is one that has a high chance of appearing in the final exam ("Write a program in C to ...").


Table of Contents

Unit Topic Programs
I Introduction to Data Structure Array traversal with step count, Divide & Conquer Max-Min, Linear vs Binary step comparison
II Recursion Factorial, Fibonacci, TOH, GCD, Sum/Reverse of digits, Indirect recursion, Tail recursion
III Stacks Stack using array, Stack using linked list, Reverse string, Balanced parentheses, Infix→Postfix, Postfix evaluation
IV Queue Linear queue, Circular queue, Priority queue, Queue using linked list
V Linked List Singly linked list (all operations), Reverse a list, Doubly linked list, Circular linked list, Concatenation, Polynomial addition
VI Trees Binary tree + 3 traversals, Level order, Height/Count/Leaf, BST (insert/search/delete), AVL tree, Huffman coding, B-Tree
VII Sorting Bubble, Insertion, Selection, Quick, Merge, Shell, Binary insertion sort, All-in-one menu
VIII Searching Sequential, Binary (iterative + recursive), BST search, Linear probing, Quadratic probing, Double hashing, Chaining, Rehashing
IX Graph Adjacency matrix & list, BFS, DFS (recursive + stack), Prim, Kruskal, Dijkstra
X Growth Functions Growth rate comparison table, Operation counting

UNIT I — Introduction to Data Structure

Q1. Write a program in C to store n elements in an array, display them, and count the number of basic operations (time complexity demonstration).

/* Unit I - Program 1: Array traversal with operation counting  (O(n)) */
#include <stdio.h>

int main(void)
{
    int a[100], n, i;
    long sum = 0;
    long steps = 0;              /* counts basic operations */

    printf("Enter number of elements: ");
    scanf("%d", &n);

    printf("Enter %d elements: ", n);
    for (i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        steps++;
    }

    printf("Elements are: ");
    for (i = 0; i < n; i++) {
        printf("%d ", a[i]);
        sum += a[i];
        steps++;
    }

    printf("\nSum = %ld", sum);
    printf("\nTotal basic operations executed = %ld", steps);
    printf("\nTime complexity = O(n), Space complexity = O(n)\n");
    return 0;
}

Q2. Write a program in C to find the maximum and minimum element of an array using the Divide and Conquer approach.

/* Unit I - Program 2: Divide and Conquer Max-Min   T(n) = 2T(n/2) + 2  =>  O(n) */
#include <stdio.h>

typedef struct {
    int min;
    int max;
} Pair;

Pair maxMin(int a[], int low, int high)
{
    Pair res, left, right;
    int mid;

    if (low == high) {                       /* only one element */
        res.min = res.max = a[low];
        return res;
    }
    if (high == low + 1) {                   /* exactly two elements */
        if (a[low] < a[high]) { res.min = a[low];  res.max = a[high]; }
        else                  { res.min = a[high]; res.max = a[low];  }
        return res;
    }

    mid   = (low + high) / 2;                /* DIVIDE */
    left  = maxMin(a, low, mid);             /* CONQUER */
    right = maxMin(a, mid + 1, high);

    res.min = (left.min < right.min) ? left.min : right.min;   /* COMBINE */
    res.max = (left.max > right.max) ? left.max : right.max;
    return res;
}

int main(void)
{
    int a[] = { 45, 12, 78, 3, 99, 56, 7, 23 };
    int n = sizeof(a) / sizeof(a[0]);
    Pair r = maxMin(a, 0, n - 1);

    printf("Minimum = %d\n", r.min);
    printf("Maximum = %d\n", r.max);
    return 0;
}

Q3. Write a program in C that compares the number of comparisons made by Linear Search (incremental approach) and Binary Search (divide & conquer approach).

/* Unit I - Program 3: Incremental vs Divide-and-Conquer  (O(n) vs O(log n)) */
#include <stdio.h>

int linearSearch(int a[], int n, int key, int *cmp)
{
    int i;
    *cmp = 0;
    for (i = 0; i < n; i++) {
        (*cmp)++;
        if (a[i] == key) return i;
    }
    return -1;
}

int binarySearch(int a[], int n, int key, int *cmp)
{
    int low = 0, high = n - 1, mid;
    *cmp = 0;
    while (low <= high) {
        mid = low + (high - low) / 2;
        (*cmp)++;
        if (a[mid] == key)      return mid;
        else if (a[mid] < key)  low  = mid + 1;
        else                    high = mid - 1;
    }
    return -1;
}

int main(void)
{
    int a[] = { 2, 5, 8, 12, 16, 23, 38, 56, 72, 91 };   /* sorted */
    int n = sizeof(a) / sizeof(a[0]);
    int key = 91, c1, c2, p1, p2;

    p1 = linearSearch(a, n, key, &c1);
    p2 = binarySearch(a, n, key, &c2);

    printf("Searching for %d in %d elements\n\n", key, n);
    printf("Linear Search : index = %d, comparisons = %d  -> O(n)\n", p1, c1);
    printf("Binary Search : index = %d, comparisons = %d  -> O(log n)\n", p2, c2);
    return 0;
}

UNIT II — Recursion

Q1. Write a program in C to find the factorial of a number using recursion (and iteration).

/* Unit II - Program 1: Factorial - recursive and iterative */
#include <stdio.h>

long factRecursive(int n)
{
    if (n == 0 || n == 1)          /* base case */
        return 1;
    return n * factRecursive(n - 1);   /* recursive case */
}

long factIterative(int n)
{
    long f = 1;
    int i;
    for (i = 2; i <= n; i++)
        f = f * i;
    return f;
}

int main(void)
{
    int n;
    printf("Enter a number: ");
    scanf("%d", &n);

    if (n < 0) {
        printf("Factorial of a negative number is not defined.\n");
        return 1;
    }
    printf("Factorial (recursive) of %d = %ld\n", n, factRecursive(n));
    printf("Factorial (iterative) of %d = %ld\n", n, factIterative(n));
    return 0;
}

Q2. Write a program in C to generate the Fibonacci series using recursion.

/* Unit II - Program 2: Fibonacci series using recursion  (O(2^n)) and iteration (O(n)) */
#include <stdio.h>

int fib(int n)
{
    if (n == 0) return 0;          /* base case 1 */
    if (n == 1) return 1;          /* base case 2 */
    return fib(n - 1) + fib(n - 2);
}

int main(void)
{
    int n, i;
    int a = 0, b = 1, c;

    printf("How many terms? ");
    scanf("%d", &n);

    printf("Fibonacci series (recursive): ");
    for (i = 0; i < n; i++)
        printf("%d ", fib(i));

    printf("\nFibonacci series (iterative): ");
    for (i = 0; i < n; i++) {
        printf("%d ", a);
        c = a + b;
        a = b;
        b = c;
    }
    printf("\n");
    return 0;
}

Q3. Write a program in C to solve the Tower of Hanoi problem using recursion.

/* Unit II - Program 3: Tower of Hanoi   -> moves = 2^n - 1 */
#include <stdio.h>

int moves = 0;

void towerOfHanoi(int n, char source, char aux, char dest)
{
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", source, dest);
        moves++;
        return;
    }
    towerOfHanoi(n - 1, source, dest, aux);        /* move n-1 to auxiliary */
    printf("Move disk %d from %c to %c\n", n, source, dest);
    moves++;
    towerOfHanoi(n - 1, aux, source, dest);        /* move n-1 to destination */
}

int main(void)
{
    int n;
    printf("Enter number of disks: ");
    scanf("%d", &n);

    towerOfHanoi(n, 'A', 'B', 'C');    /* A = source, B = auxiliary, C = destination */
    printf("\nTotal moves = %d  (2^%d - 1)\n", moves, n);
    return 0;
}

Q4. Write a program in C to find the GCD of two numbers using recursion.

/* Unit II - Program 4: GCD using recursion (Euclid's algorithm) */
#include <stdio.h>

int gcd(int a, int b)
{
    if (b == 0)
        return a;               /* base case */
    return gcd(b, a % b);       /* tail recursion */
}

int main(void)
{
    int a, b;
    printf("Enter two numbers: ");
    scanf("%d %d", &a, &b);
    printf("GCD(%d, %d) = %d\n", a, b, gcd(a, b));
    printf("LCM(%d, %d) = %d\n", a, b, (a * b) / gcd(a, b));
    return 0;
}

Q5. Write a program in C to find the sum of digits and reverse of a number using recursion.

/* Unit II - Program 5: Sum of digits and reverse of a number using recursion */
#include <stdio.h>

int sumOfDigits(int n)
{
    if (n == 0) return 0;
    return (n % 10) + sumOfDigits(n / 10);
}

int reverseNumber(int n, int rev)
{
    if (n == 0) return rev;
    return reverseNumber(n / 10, rev * 10 + n % 10);
}

int power(int base, int exp)
{
    if (exp == 0) return 1;
    return base * power(base, exp - 1);
}

int main(void)
{
    int n, b, e;
    printf("Enter a number: ");
    scanf("%d", &n);
    printf("Sum of digits = %d\n", sumOfDigits(n));
    printf("Reverse       = %d\n", reverseNumber(n, 0));

    printf("Enter base and exponent: ");
    scanf("%d %d", &b, &e);
    printf("%d ^ %d = %d\n", b, e, power(b, e));
    return 0;
}

Q6. Write a program in C to demonstrate Direct, Indirect, Linear and Tail recursion.

/* Unit II - Program 6: Types of recursion */
#include <stdio.h>

/* 1. DIRECT + LINEAR recursion : function calls itself once */
int sumN(int n)
{
    if (n == 0) return 0;
    return n + sumN(n - 1);
}

/* 2. TAIL recursion : recursive call is the LAST statement */
int sumTail(int n, int acc)
{
    if (n == 0) return acc;
    return sumTail(n - 1, acc + n);
}

/* 3. INDIRECT recursion : isEven -> isOdd -> isEven ... */
int isOdd(int n);                     /* forward declaration */

int isEven(int n)
{
    if (n == 0) return 1;
    return isOdd(n - 1);
}

int isOdd(int n)
{
    if (n == 0) return 0;
    return isEven(n - 1);
}

/* 4. TREE (non-linear) recursion : more than one recursive call */
int fibTree(int n)
{
    if (n < 2) return n;
    return fibTree(n - 1) + fibTree(n - 2);
}

int main(void)
{
    int n = 5;
    printf("Direct/Linear  : sum of 1..%d = %d\n", n, sumN(n));
    printf("Tail recursion : sum of 1..%d = %d\n", n, sumTail(n, 0));
    printf("Indirect       : %d is %s\n", n, isEven(n) ? "Even" : "Odd");
    printf("Tree recursion : fib(%d) = %d\n", n, fibTree(n));
    return 0;
}

UNIT III — Stacks

Q1. Write a program in C to implement a stack using an array with PUSH, POP, PEEK and DISPLAY operations.

/* Unit III - Program 1: Stack using array (menu driven) */
#include <stdio.h>
#include <stdlib.h>
#define MAX 5

int stack[MAX];
int top = -1;

void push(int item)
{
    if (top == MAX - 1) {                 /* OVERFLOW */
        printf("Stack Overflow! Cannot push %d\n", item);
        return;
    }
    top = top + 1;
    stack[top] = item;
    printf("%d pushed into stack\n", item);
}

int pop(void)
{
    int item;
    if (top == -1) {                      /* UNDERFLOW */
        printf("Stack Underflow!\n");
        return -1;
    }
    item = stack[top];
    top = top - 1;
    return item;
}

int peek(void)
{
    if (top == -1) {
        printf("Stack is empty!\n");
        return -1;
    }
    return stack[top];
}

void display(void)
{
    int i;
    if (top == -1) {
        printf("Stack is empty!\n");
        return;
    }
    printf("Stack (top to bottom): ");
    for (i = top; i >= 0; i--)
        printf("%d ", stack[i]);
    printf("\n");
}

int main(void)
{
    int choice, item;

    while (1) {
        printf("\n1.Push  2.Pop  3.Peek  4.Display  5.Exit\nEnter choice: ");
        if (scanf("%d", &choice) != 1) break;

        switch (choice) {
        case 1:
            printf("Enter item to push: ");
            scanf("%d", &item);
            push(item);
            break;
        case 2:
            item = pop();
            if (item != -1) printf("Popped item = %d\n", item);
            break;
        case 3:
            item = peek();
            if (item != -1) printf("Top item = %d\n", item);
            break;
        case 4:
            display();
            break;
        case 5:
            exit(0);
        default:
            printf("Invalid choice!\n");
        }
    }
    return 0;
}

Q2. Write a program in C to implement a stack using a linked list.

/* Unit III - Program 2: Stack using linked list (dynamic stack) */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *top = NULL;

void push(int item)
{
    struct Node *newNode = (struct Node *) malloc(sizeof(struct Node));
    if (newNode == NULL) {
        printf("Memory not available (Overflow)\n");
        return;
    }
    newNode->data = item;
    newNode->next = top;      /* new node points to old top */
    top = newNode;            /* new node becomes top       */
    printf("%d pushed\n", item);
}

int pop(void)
{
    struct Node *temp;
    int item;
    if (top == NULL) {
        printf("Stack Underflow!\n");
        return -1;
    }
    temp = top;
    item = temp->data;
    top  = top->next;
    free(temp);
    return item;
}

void display(void)
{
    struct Node *temp = top;
    if (temp == NULL) { printf("Stack is empty\n"); return; }
    printf("Stack (top to bottom): ");
    while (temp != NULL) {
        printf("%d ", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

int main(void)
{
    push(10); push(20); push(30);
    display();
    printf("Popped = %d\n", pop());
    display();
    printf("Top element = %d\n", top ? top->data : -1);
    return 0;
}

Q3. Write a program in C to reverse a string using a stack.

/* Unit III - Program 3: Reverse a string using stack */
#include <stdio.h>
#include <string.h>
#define MAX 100

char stack[MAX];
int top = -1;

void push(char c) { stack[++top] = c; }
char pop(void)    { return stack[top--]; }
int isEmpty(void) { return (top == -1); }

int main(void)
{
    char str[MAX];
    int i, len;

    printf("Enter a string: ");
    scanf("%99s", str);
    len = strlen(str);

    for (i = 0; i < len; i++)          /* push all characters */
        push(str[i]);

    printf("Reversed string: ");
    while (!isEmpty())                 /* pop => reverse order (LIFO) */
        printf("%c", pop());
    printf("\n");
    return 0;
}

Q4. Write a program in C to check whether an expression has balanced parentheses using a stack.

/* Unit III - Program 4: Balanced parentheses checking using stack */
#include <stdio.h>
#include <string.h>
#define MAX 100

char stack[MAX];
int top = -1;

void push(char c) { stack[++top] = c; }
char pop(void)    { return (top == -1) ? '\0' : stack[top--]; }
int isEmpty(void) { return (top == -1); }

int isMatch(char open, char close)
{
    return (open == '(' && close == ')') ||
           (open == '[' && close == ']') ||
           (open == '{' && close == '}');
}

int main(void)
{
    char exp[MAX], ch;
    int i, balanced = 1;

    printf("Enter an expression: ");
    scanf("%99s", exp);

    for (i = 0; exp[i] != '\0'; i++) {
        ch = exp[i];
        if (ch == '(' || ch == '[' || ch == '{')
            push(ch);
        else if (ch == ')' || ch == ']' || ch == '}') {
            if (isEmpty() || !isMatch(pop(), ch)) { balanced = 0; break; }
        }
    }
    if (!isEmpty()) balanced = 0;

    printf("Expression is %s\n", balanced ? "BALANCED" : "NOT BALANCED");
    return 0;
}

Q5. Write a program in C to convert an infix expression into its postfix form using a stack.

/* Unit III - Program 5: Infix to Postfix conversion using stack */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAX 100

char stack[MAX];
int top = -1;

void push(char c) { stack[++top] = c; }
char pop(void)    { return stack[top--]; }
char peek(void)   { return stack[top]; }
int isEmpty(void) { return (top == -1); }

int precedence(char c)
{
    if (c == '^')                            return 3;
    if (c == '*' || c == '/' || c == '%')     return 2;
    if (c == '+' || c == '-')                 return 1;
    return 0;
}

int main(void)
{
    char infix[MAX], postfix[MAX], ch;
    int i, j = 0;

    printf("Enter infix expression (no spaces): ");
    scanf("%99s", infix);

    for (i = 0; infix[i] != '\0'; i++) {
        ch = infix[i];

        if (isalnum(ch)) {                       /* operand -> output */
            postfix[j++] = ch;
        }
        else if (ch == '(') {
            push(ch);
        }
        else if (ch == ')') {                    /* pop till '(' */
            while (!isEmpty() && peek() != '(')
                postfix[j++] = pop();
            if (!isEmpty()) pop();               /* discard '(' */
        }
        else {                                   /* operator */
            while (!isEmpty() && peek() != '(' &&
                   (precedence(peek()) > precedence(ch) ||
                   (precedence(peek()) == precedence(ch) && ch != '^')))
                postfix[j++] = pop();
            push(ch);
        }
    }
    while (!isEmpty())                           /* pop remaining */
        postfix[j++] = pop();
    postfix[j] = '\0';

    printf("Infix   : %s\n", infix);
    printf("Postfix : %s\n", postfix);
    return 0;
}

/* Sample: input  a+b*c-(d/e+f)*g
           output abc*+de/f+g*-                                     */

Q6. Write a program in C to evaluate a postfix expression using a stack.

/* Unit III - Program 6: Evaluation of postfix expression (single digit operands) */
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#define MAX 100

int stack[MAX];
int top = -1;

void push(int x) { stack[++top] = x; }
int  pop(void)   { return stack[top--]; }

int main(void)
{
    char postfix[MAX], ch;
    int i, op1, op2, result;

    printf("Enter postfix expression (single digits, e.g. 53+82-*): ");
    scanf("%99s", postfix);

    for (i = 0; postfix[i] != '\0'; i++) {
        ch = postfix[i];

        if (isdigit(ch)) {
            push(ch - '0');                 /* char to int */
        } else {
            op2 = pop();                    /* second operand popped first */
            op1 = pop();
            switch (ch) {
            case '+': push(op1 + op2); break;
            case '-': push(op1 - op2); break;
            case '*': push(op1 * op2); break;
            case '/': push(op1 / op2); break;
            case '^': push((int) pow(op1, op2)); break;
            default : printf("Invalid operator %c\n", ch); return 1;
            }
        }
    }
    result = pop();
    printf("Result = %d\n", result);
    return 0;
}
/* compile with: gcc prog.c -o prog -lm
   Sample: 53+82-*  =>  (5+3)*(8-2) = 48                            */

UNIT IV — Queue

Q1. Write a program in C to implement a linear queue using an array (insertion and deletion).

/* Unit IV - Program 1: Linear (simple) queue using array */
#include <stdio.h>
#include <stdlib.h>
#define MAX 5

int queue[MAX];
int front = -1, rear = -1;

void enqueue(int item)
{
    if (rear == MAX - 1) {                       /* queue full */
        printf("Queue Overflow! Cannot insert %d\n", item);
        return;
    }
    if (front == -1) front = 0;                  /* first insertion */
    queue[++rear] = item;
    printf("%d inserted\n", item);
}

int dequeue(void)
{
    int item;
    if (front == -1 || front > rear) {           /* queue empty */
        printf("Queue Underflow!\n");
        return -1;
    }
    item = queue[front++];
    if (front > rear) front = rear = -1;         /* reset when empty */
    return item;
}

void display(void)
{
    int i;
    if (front == -1) { printf("Queue is empty\n"); return; }
    printf("Queue (front to rear): ");
    for (i = front; i <= rear; i++)
        printf("%d ", queue[i]);
    printf("\n");
}

int main(void)
{
    int choice, item;
    while (1) {
        printf("\n1.Insert  2.Delete  3.Display  4.Exit\nEnter choice: ");
        if (scanf("%d", &choice) != 1) break;
        switch (choice) {
        case 1: printf("Enter item: "); scanf("%d", &item); enqueue(item); break;
        case 2: item = dequeue();
                if (item != -1) printf("Deleted item = %d\n", item);
                break;
        case 3: display(); break;
        case 4: exit(0);
        default: printf("Invalid choice\n");
        }
    }
    return 0;
}
/* Limitation of simple queue: after deletions the front slots cannot be
   reused even though they are free  ->  solved by CIRCULAR QUEUE          */

Q2. Write a program in C to implement a circular queue.

/* Unit IV - Program 2: Circular queue using array */
#include <stdio.h>
#include <stdlib.h>
#define MAX 5

int cq[MAX];
int front = -1, rear = -1;

void enqueue(int item)
{
    if ((front == 0 && rear == MAX - 1) || (rear + 1) % MAX == front) {
        printf("Circular Queue Overflow! Cannot insert %d\n", item);
        return;
    }
    if (front == -1) { front = rear = 0; }
    else             { rear = (rear + 1) % MAX; }     /* wrap around */
    cq[rear] = item;
    printf("%d inserted at position %d\n", item, rear);
}

int dequeue(void)
{
    int item;
    if (front == -1) { printf("Circular Queue Underflow!\n"); return -1; }
    item = cq[front];
    if (front == rear) front = rear = -1;             /* last element */
    else               front = (front + 1) % MAX;
    return item;
}

void display(void)
{
    int i;
    if (front == -1) { printf("Queue is empty\n"); return; }
    printf("Circular Queue: ");
    i = front;
    while (1) {
        printf("%d ", cq[i]);
        if (i == rear) break;
        i = (i + 1) % MAX;
    }
    printf("\n");
}

int main(void)
{
    int choice, item;
    while (1) {
        printf("\n1.Insert  2.Delete  3.Display  4.Exit\nEnter choice: ");
        if (scanf("%d", &choice) != 1) break;
        switch (choice) {
        case 1: printf("Enter item: "); scanf("%d", &item); enqueue(item); break;
        case 2: item = dequeue();
                if (item != -1) printf("Deleted item = %d\n", item);
                break;
        case 3: display(); break;
        case 4: exit(0);
        default: printf("Invalid choice\n");
        }
    }
    return 0;
}

Q3. Write a program in C to implement a priority queue.

/* Unit IV - Program 3: Priority queue using array (lower number = higher priority) */
#include <stdio.h>
#include <stdlib.h>
#define MAX 20

struct Item {
    int data;
    int priority;
};

struct Item pq[MAX];
int size = 0;

void insert(int data, int priority)
{
    int i;
    if (size == MAX) { printf("Priority Queue is Full\n"); return; }

    /* find correct position (sorted by priority) */
    i = size - 1;
    while (i >= 0 && pq[i].priority > priority) {
        pq[i + 1] = pq[i];
        i--;
    }
    pq[i + 1].data = data;
    pq[i + 1].priority = priority;
    size++;
    printf("Inserted data=%d with priority=%d\n", data, priority);
}

void deleteHighest(void)
{
    int i;
    if (size == 0) { printf("Priority Queue is Empty\n"); return; }
    printf("Deleted data=%d (priority=%d)\n", pq[0].data, pq[0].priority);
    for (i = 0; i < size - 1; i++)
        pq[i] = pq[i + 1];
    size--;
}

void display(void)
{
    int i;
    if (size == 0) { printf("Priority Queue is Empty\n"); return; }
    printf("Priority Queue (data:priority) -> ");
    for (i = 0; i < size; i++)
        printf("%d:%d  ", pq[i].data, pq[i].priority);
    printf("\n");
}

int main(void)
{
    insert(100, 3);
    insert(200, 1);
    insert(300, 4);
    insert(400, 2);
    display();
    deleteHighest();
    deleteHighest();
    display();
    return 0;
}

Q4. Write a program in C to implement a queue using a linked list.

/* Unit IV - Program 4: Queue using linked list */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *front = NULL, *rear = NULL;

void enqueue(int item)
{
    struct Node *newNode = (struct Node *) malloc(sizeof(struct Node));
    newNode->data = item;
    newNode->next = NULL;

    if (rear == NULL) {                /* empty queue */
        front = rear = newNode;
    } else {
        rear->next = newNode;
        rear = newNode;
    }
    printf("%d inserted\n", item);
}

int dequeue(void)
{
    struct Node *temp;
    int item;
    if (front == NULL) { printf("Queue Underflow\n"); return -1; }

    temp  = front;
    item  = temp->data;
    front = front->next;
    if (front == NULL) rear = NULL;    /* queue became empty */
    free(temp);
    return item;
}

void display(void)
{
    struct Node *temp = front;
    if (temp == NULL) { printf("Queue is empty\n"); return; }
    printf("Queue (front to rear): ");
    while (temp != NULL) { printf("%d ", temp->data); temp = temp->next; }
    printf("\n");
}

int main(void)
{
    enqueue(11); enqueue(22); enqueue(33);
    display();
    printf("Deleted = %d\n", dequeue());
    display();
    return 0;
}

UNIT V — Linked List

Q1. Write a program in C to create a singly linked list and perform creation, insertion (beginning / end / specific position), deletion (beginning / end / specific position), traversing, searching and display.

/* Unit V - Program 1: Singly Linked List - ALL operations (most important) */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *head = NULL;

struct Node *createNode(int data)
{
    struct Node *newNode = (struct Node *) malloc(sizeof(struct Node));
    if (newNode == NULL) { printf("Memory allocation failed\n"); exit(1); }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

/* ---------- INSERTION ---------- */
void insertAtBeginning(int data)
{
    struct Node *newNode = createNode(data);
    newNode->next = head;
    head = newNode;
}

void insertAtEnd(int data)
{
    struct Node *newNode = createNode(data);
    struct Node *temp;
    if (head == NULL) { head = newNode; return; }
    temp = head;
    while (temp->next != NULL) temp = temp->next;
    temp->next = newNode;
}

void insertAtPosition(int data, int pos)      /* pos starts from 1 */
{
    struct Node *newNode, *temp;
    int i;
    if (pos < 1) { printf("Invalid position\n"); return; }
    if (pos == 1) { insertAtBeginning(data); return; }

    temp = head;
    for (i = 1; i < pos - 1 && temp != NULL; i++)
        temp = temp->next;

    if (temp == NULL) { printf("Position out of range\n"); return; }
    newNode = createNode(data);
    newNode->next = temp->next;
    temp->next = newNode;
}

/* ---------- DELETION ---------- */
void deleteFromBeginning(void)
{
    struct Node *temp;
    if (head == NULL) { printf("List is empty\n"); return; }
    temp = head;
    head = head->next;
    printf("Deleted %d\n", temp->data);
    free(temp);
}

void deleteFromEnd(void)
{
    struct Node *temp = head, *prev = NULL;
    if (head == NULL) { printf("List is empty\n"); return; }
    if (head->next == NULL) {
        printf("Deleted %d\n", head->data);
        free(head); head = NULL; return;
    }
    while (temp->next != NULL) { prev = temp; temp = temp->next; }
    prev->next = NULL;
    printf("Deleted %d\n", temp->data);
    free(temp);
}

void deleteFromPosition(int pos)
{
    struct Node *temp = head, *prev = NULL;
    int i;
    if (head == NULL) { printf("List is empty\n"); return; }
    if (pos == 1) { deleteFromBeginning(); return; }

    for (i = 1; i < pos && temp != NULL; i++) { prev = temp; temp = temp->next; }
    if (temp == NULL) { printf("Position out of range\n"); return; }
    prev->next = temp->next;
    printf("Deleted %d\n", temp->data);
    free(temp);
}

/* ---------- SEARCH / TRAVERSE / COUNT ---------- */
int search(int key)
{
    struct Node *temp = head;
    int pos = 1;
    while (temp != NULL) {
        if (temp->data == key) return pos;
        temp = temp->next;
        pos++;
    }
    return -1;
}

int countNodes(void)
{
    struct Node *temp = head;
    int count = 0;
    while (temp != NULL) { count++; temp = temp->next; }
    return count;
}

void display(void)
{
    struct Node *temp = head;
    if (head == NULL) { printf("List is empty\n"); return; }
    printf("List: ");
    while (temp != NULL) {
        printf("%d -> ", temp->data);
        temp = temp->next;
    }
    printf("NULL\n");
}

int main(void)
{
    int choice, data, pos;

    while (1) {
        printf("\n--- SINGLY LINKED LIST ---\n");
        printf("1.Insert at beginning 2.Insert at end 3.Insert at position\n");
        printf("4.Delete from beginning 5.Delete from end 6.Delete from position\n");
        printf("7.Search 8.Count 9.Display 10.Exit\nEnter choice: ");
        if (scanf("%d", &choice) != 1) break;

        switch (choice) {
        case 1: printf("Enter data: "); scanf("%d", &data);
                insertAtBeginning(data); break;
        case 2: printf("Enter data: "); scanf("%d", &data);
                insertAtEnd(data); break;
        case 3: printf("Enter data and position: "); scanf("%d %d", &data, &pos);
                insertAtPosition(data, pos); break;
        case 4: deleteFromBeginning(); break;
        case 5: deleteFromEnd(); break;
        case 6: printf("Enter position: "); scanf("%d", &pos);
                deleteFromPosition(pos); break;
        case 7: printf("Enter key to search: "); scanf("%d", &data);
                pos = search(data);
                if (pos == -1) printf("%d not found\n", data);
                else printf("%d found at position %d\n", data, pos);
                break;
        case 8: printf("Total nodes = %d\n", countNodes()); break;
        case 9: display(); break;
        case 10: exit(0);
        default: printf("Invalid choice\n");
        }
    }
    return 0;
}

Q2. Write a program in C to reverse a singly linked list.

/* Unit V - Program 2: Reverse a singly linked list (iterative + recursive) */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *head = NULL;

void insertAtEnd(int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    struct Node *temp = head;
    newNode->data = data;
    newNode->next = NULL;
    if (head == NULL) { head = newNode; return; }
    while (temp->next != NULL) temp = temp->next;
    temp->next = newNode;
}

/* Iterative reversal - O(n) time, O(1) space */
void reverseIterative(void)
{
    struct Node *prev = NULL, *current = head, *nextNode = NULL;
    while (current != NULL) {
        nextNode = current->next;    /* save next   */
        current->next = prev;        /* reverse link */
        prev = current;              /* move prev   */
        current = nextNode;          /* move current */
    }
    head = prev;
}

/* Recursive reversal */
struct Node *reverseRecursive(struct Node *node)
{
    struct Node *rest;
    if (node == NULL || node->next == NULL) return node;
    rest = reverseRecursive(node->next);
    node->next->next = node;
    node->next = NULL;
    return rest;
}

void display(void)
{
    struct Node *temp = head;
    while (temp != NULL) { printf("%d -> ", temp->data); temp = temp->next; }
    printf("NULL\n");
}

int main(void)
{
    insertAtEnd(10); insertAtEnd(20); insertAtEnd(30);
    insertAtEnd(40); insertAtEnd(50);

    printf("Original list  : "); display();
    reverseIterative();
    printf("After iterative: "); display();
    head = reverseRecursive(head);
    printf("After recursive: "); display();
    return 0;
}

Q3. Write a program in C to implement a doubly linked list with insertion, deletion and traversal (forward and backward).

/* Unit V - Program 3: Doubly Linked List */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *prev;
    struct Node *next;
};

struct Node *head = NULL;

struct Node *createNode(int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->prev = newNode->next = NULL;
    return newNode;
}

void insertAtBeginning(int data)
{
    struct Node *newNode = createNode(data);
    if (head != NULL) { newNode->next = head; head->prev = newNode; }
    head = newNode;
}

void insertAtEnd(int data)
{
    struct Node *newNode = createNode(data), *temp = head;
    if (head == NULL) { head = newNode; return; }
    while (temp->next != NULL) temp = temp->next;
    temp->next = newNode;
    newNode->prev = temp;
}

void deleteNode(int key)
{
    struct Node *temp = head;
    while (temp != NULL && temp->data != key) temp = temp->next;
    if (temp == NULL) { printf("%d not found\n", key); return; }

    if (temp->prev != NULL) temp->prev->next = temp->next;
    else                    head = temp->next;          /* deleting head */
    if (temp->next != NULL) temp->next->prev = temp->prev;

    printf("Deleted %d\n", temp->data);
    free(temp);
}

void displayForward(void)
{
    struct Node *temp = head;
    printf("Forward : NULL <-> ");
    while (temp != NULL) { printf("%d <-> ", temp->data); temp = temp->next; }
    printf("NULL\n");
}

void displayBackward(void)
{
    struct Node *temp = head;
    if (temp == NULL) { printf("List empty\n"); return; }
    while (temp->next != NULL) temp = temp->next;        /* go to last */
    printf("Backward: NULL <-> ");
    while (temp != NULL) { printf("%d <-> ", temp->data); temp = temp->prev; }
    printf("NULL\n");
}

int main(void)
{
    insertAtEnd(20); insertAtEnd(30); insertAtEnd(40);
    insertAtBeginning(10);
    displayForward();
    displayBackward();
    deleteNode(30);
    displayForward();
    return 0;
}

Q4. Write a program in C to implement a circular linked list (insertion, deletion, display).

/* Unit V - Program 4: Circular Linked List (last node points to first node) */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

struct Node *last = NULL;      /* pointer to last node */

void insertAtEnd(int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->data = data;
    if (last == NULL) {
        newNode->next = newNode;      /* points to itself */
        last = newNode;
        return;
    }
    newNode->next = last->next;       /* new node -> first node */
    last->next = newNode;
    last = newNode;                   /* new node becomes last  */
}

void insertAtBeginning(int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->data = data;
    if (last == NULL) { newNode->next = newNode; last = newNode; return; }
    newNode->next = last->next;
    last->next = newNode;
}

void deleteNode(int key)
{
    struct Node *current, *prev;
    if (last == NULL) { printf("List is empty\n"); return; }

    current = last->next;             /* first node */
    prev = last;
    do {
        if (current->data == key) {
            if (current == last && current->next == last) {   /* only node */
                free(current); last = NULL;
            } else {
                prev->next = current->next;
                if (current == last) last = prev;
                free(current);
            }
            printf("Deleted %d\n", key);
            return;
        }
        prev = current;
        current = current->next;
    } while (current != last->next);

    printf("%d not found\n", key);
}

void display(void)
{
    struct Node *temp;
    if (last == NULL) { printf("List is empty\n"); return; }
    temp = last->next;
    printf("Circular List: ");
    do {
        printf("%d -> ", temp->data);
        temp = temp->next;
    } while (temp != last->next);
    printf("(back to first)\n");
}

int main(void)
{
    insertAtEnd(10); insertAtEnd(20); insertAtEnd(30);
    insertAtBeginning(5);
    display();
    deleteNode(20);
    display();
    return 0;
}

Q5. Write a program in C to concatenate two singly linked lists.

/* Unit V - Program 5: Concatenation of two linked lists */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *next;
};

void insertAtEnd(struct Node **head, int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    struct Node *temp = *head;
    newNode->data = data;
    newNode->next = NULL;
    if (*head == NULL) { *head = newNode; return; }
    while (temp->next != NULL) temp = temp->next;
    temp->next = newNode;
}

struct Node *concatenate(struct Node *first, struct Node *second)
{
    struct Node *temp;
    if (first == NULL)  return second;
    if (second == NULL) return first;
    temp = first;
    while (temp->next != NULL) temp = temp->next;   /* last node of first */
    temp->next = second;                            /* link to second     */
    return first;
}

void display(struct Node *head)
{
    while (head != NULL) { printf("%d -> ", head->data); head = head->next; }
    printf("NULL\n");
}

int main(void)
{
    struct Node *list1 = NULL, *list2 = NULL, *result;

    insertAtEnd(&list1, 1); insertAtEnd(&list1, 2); insertAtEnd(&list1, 3);
    insertAtEnd(&list2, 7); insertAtEnd(&list2, 8); insertAtEnd(&list2, 9);

    printf("List 1: "); display(list1);
    printf("List 2: "); display(list2);

    result = concatenate(list1, list2);
    printf("Concatenated: "); display(result);
    return 0;
}

Q6. Write a program in C to add two polynomials using a linked list.

/* Unit V - Program 6: Addition of two polynomials using linked list
   (VERY IMPORTANT - application of linked list)                       */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int coef;              /* coefficient */
    int exp;               /* exponent    */
    struct Node *next;
};

/* insert term at end (terms kept in decreasing order of exponent) */
void insertTerm(struct Node **head, int coef, int exp)
{
    struct Node *newNode, *temp;
    if (coef == 0) return;
    newNode = malloc(sizeof(struct Node));
    newNode->coef = coef;
    newNode->exp  = exp;
    newNode->next = NULL;

    if (*head == NULL) { *head = newNode; return; }
    temp = *head;
    while (temp->next != NULL) temp = temp->next;
    temp->next = newNode;
}

struct Node *addPolynomials(struct Node *p1, struct Node *p2)
{
    struct Node *result = NULL;

    while (p1 != NULL && p2 != NULL) {
        if (p1->exp > p2->exp) {
            insertTerm(&result, p1->coef, p1->exp);
            p1 = p1->next;
        } else if (p1->exp < p2->exp) {
            insertTerm(&result, p2->coef, p2->exp);
            p2 = p2->next;
        } else {                                    /* same exponent -> add */
            insertTerm(&result, p1->coef + p2->coef, p1->exp);
            p1 = p1->next;
            p2 = p2->next;
        }
    }
    while (p1 != NULL) { insertTerm(&result, p1->coef, p1->exp); p1 = p1->next; }
    while (p2 != NULL) { insertTerm(&result, p2->coef, p2->exp); p2 = p2->next; }

    return result;
}

void display(struct Node *p)
{
    if (p == NULL) { printf("0\n"); return; }
    while (p != NULL) {
        printf("%dx^%d", p->coef, p->exp);
        if (p->next != NULL) printf(" + ");
        p = p->next;
    }
    printf("\n");
}

int main(void)
{
    struct Node *poly1 = NULL, *poly2 = NULL, *sum;

    /* poly1 = 5x^3 + 4x^2 + 2x^0 */
    insertTerm(&poly1, 5, 3);
    insertTerm(&poly1, 4, 2);
    insertTerm(&poly1, 2, 0);

    /* poly2 = 5x^4 + 3x^2 + 4x^1 */
    insertTerm(&poly2, 5, 4);
    insertTerm(&poly2, 3, 2);
    insertTerm(&poly2, 4, 1);

    printf("Polynomial 1 : "); display(poly1);
    printf("Polynomial 2 : "); display(poly2);

    sum = addPolynomials(poly1, poly2);
    printf("Sum          : "); display(sum);
    return 0;
}
/* Output: 5x^4 + 5x^3 + 7x^2 + 4x^1 + 2x^0 */

UNIT VI — Trees

Q1. Write a program in C to create a binary tree using linked representation and display its Preorder, Inorder and Postorder traversal.

/* Unit VI - Program 1: Binary tree creation + 3 traversals (recursive) */
#include <stdio.h>
#include <stdlib.h>

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

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

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

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

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

/* create tree by asking user (enter -1 for no child) */
struct Node *buildTree(void)
{
    int data;
    struct Node *root;

    printf("Enter data (-1 for no node): ");
    scanf("%d", &data);
    if (data == -1) return NULL;

    root = createNode(data);
    printf("Enter left child of %d\n", data);
    root->left = buildTree();
    printf("Enter right child of %d\n", data);
    root->right = buildTree();
    return root;
}

int main(void)
{
    struct Node *root = buildTree();

    printf("\nPreorder  : "); preorder(root);
    printf("\nInorder   : "); inorder(root);
    printf("\nPostorder : "); postorder(root);
    printf("\n");
    return 0;
}
/*        1
        /   \
       2     3      Preorder : 1 2 4 5 3
      / \           Inorder  : 4 2 5 1 3
     4   5          Postorder: 4 5 2 3 1
   Input sequence: 1 2 4 -1 -1 5 -1 -1 3 -1 -1                      */

Q2. Write a program in C to find the height, total number of nodes, and number of leaf nodes of a binary tree, and print the level order traversal.

/* Unit VI - Program 2: Height, Depth, Level order traversal, Node counts */
#include <stdio.h>
#include <stdlib.h>
#define MAX 100

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

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

int height(struct Node *root)              /* height of tree = max depth */
{
    int lh, rh;
    if (root == NULL) return -1;           /* height of empty tree = -1  */
    lh = height(root->left);
    rh = height(root->right);
    return (lh > rh ? lh : rh) + 1;
}

int countNodes(struct Node *root)
{
    if (root == NULL) return 0;
    return 1 + countNodes(root->left) + countNodes(root->right);
}

int countLeaves(struct Node *root)
{
    if (root == NULL) return 0;
    if (root->left == NULL && root->right == NULL) return 1;
    return countLeaves(root->left) + countLeaves(root->right);
}

int countInternal(struct Node *root)
{
    if (root == NULL || (root->left == NULL && root->right == NULL)) return 0;
    return 1 + countInternal(root->left) + countInternal(root->right);
}

/* Level order traversal (BFS) using a queue */
void levelOrder(struct Node *root)
{
    struct Node *queue[MAX], *current;
    int front = 0, rear = 0;

    if (root == NULL) return;
    queue[rear++] = root;

    while (front < rear) {
        current = queue[front++];
        printf("%d ", current->data);
        if (current->left  != NULL) queue[rear++] = current->left;
        if (current->right != NULL) queue[rear++] = current->right;
    }
}

int main(void)
{
    /*         1
             /   \
            2     3
           / \     \
          4   5     6      */
    struct 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("Level order traversal : "); levelOrder(root);
    printf("\nHeight of tree       : %d", height(root));
    printf("\nTotal nodes          : %d", countNodes(root));
    printf("\nLeaf nodes           : %d", countLeaves(root));
    printf("\nInternal nodes       : %d\n", countInternal(root));
    return 0;
}

Q3. Write a program in C to create a Binary Search Tree and perform insertion, searching and deletion.

/* Unit VI - Program 3: Binary Search Tree - Insert, Search, Delete (MOST IMPORTANT) */
#include <stdio.h>
#include <stdlib.h>

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

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

/* ---------------- INSERTION ---------------- */
struct Node *insert(struct 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);
    else
        printf("%d already exists\n", data);
    return root;
}

/* ---------------- SEARCHING ---------------- */
struct Node *search(struct 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);
}

/* smallest node of right subtree = inorder successor */
struct Node *findMin(struct Node *root)
{
    while (root->left != NULL) root = root->left;
    return root;
}

/* ---------------- DELETION ---------------- */
struct Node *deleteNode(struct Node *root, int key)
{
    struct Node *temp;

    if (root == NULL) { printf("%d not found\n", key); return NULL; }

    if (key < root->data)
        root->left = deleteNode(root->left, key);
    else if (key > root->data)
        root->right = deleteNode(root->right, key);
    else {
        /* CASE 1: leaf node (no child) */
        if (root->left == NULL && root->right == NULL) {
            free(root);
            return NULL;
        }
        /* CASE 2: only one child */
        else if (root->left == NULL) {
            temp = root->right; free(root); return temp;
        }
        else if (root->right == NULL) {
            temp = root->left;  free(root); return temp;
        }
        /* CASE 3: two children -> replace with inorder successor */
        else {
            temp = findMin(root->right);
            root->data = temp->data;
            root->right = deleteNode(root->right, temp->data);
        }
    }
    return root;
}

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

int main(void)
{
    struct Node *root = NULL;
    int choice, value;

    /* sample tree */
    root = insert(root, 50);
    insert(root, 30); insert(root, 70); insert(root, 20);
    insert(root, 40); insert(root, 60); insert(root, 80);

    while (1) {
        printf("\n1.Insert 2.Search 3.Delete 4.Inorder(sorted) 5.Exit\nChoice: ");
        if (scanf("%d", &choice) != 1) break;

        switch (choice) {
        case 1: printf("Enter value: "); scanf("%d", &value);
                root = insert(root, value); break;
        case 2: printf("Enter key: "); scanf("%d", &value);
                if (search(root, value)) printf("%d FOUND\n", value);
                else printf("%d NOT FOUND\n", value);
                break;
        case 3: printf("Enter key to delete: "); scanf("%d", &value);
                root = deleteNode(root, value); break;
        case 4: printf("Inorder: "); inorder(root); printf("\n"); break;
        case 5: exit(0);
        default: printf("Invalid choice\n");
        }
    }
    return 0;
}

Q4. Write a program in C to implement an AVL (height balanced) tree with insertion and rotations.

/* Unit VI - Program 4: AVL Tree insertion with LL, RR, LR, RL rotations */
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *left, *right;
    int height;
};

int height(struct Node *n)
{
    return (n == NULL) ? 0 : n->height;
}

int max(int a, int b) { return (a > b) ? a : b; }

struct Node *createNode(int data)
{
    struct Node *n = malloc(sizeof(struct Node));
    n->data = data;
    n->left = n->right = NULL;
    n->height = 1;
    return n;
}

int getBalance(struct Node *n)
{
    return (n == NULL) ? 0 : height(n->left) - height(n->right);
}

/* Right rotation (for LL imbalance) */
struct Node *rightRotate(struct Node *y)
{
    struct Node *x  = y->left;
    struct Node *T2 = x->right;

    x->right = y;
    y->left  = T2;

    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
    return x;                       /* new root */
}

/* Left rotation (for RR imbalance) */
struct Node *leftRotate(struct Node *x)
{
    struct Node *y  = x->right;
    struct Node *T2 = y->left;

    y->left  = x;
    x->right = T2;

    x->height = max(height(x->left), height(x->right)) + 1;
    y->height = max(height(y->left), height(y->right)) + 1;
    return y;                       /* new root */
}

struct Node *insert(struct Node *node, int data)
{
    int balance;

    /* 1. normal BST insertion */
    if (node == NULL) return createNode(data);

    if (data < node->data)
        node->left = insert(node->left, data);
    else if (data > node->data)
        node->right = insert(node->right, data);
    else
        return node;                /* duplicates not allowed */

    /* 2. update height */
    node->height = 1 + max(height(node->left), height(node->right));

    /* 3. get balance factor */
    balance = getBalance(node);

    /* 4. four rotation cases */
    if (balance > 1 && data < node->left->data)          /* LL */
        return rightRotate(node);

    if (balance < -1 && data > node->right->data)        /* RR */
        return leftRotate(node);

    if (balance > 1 && data > node->left->data) {        /* LR */
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    if (balance < -1 && data < node->right->data) {      /* RL */
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node;
}

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

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

int main(void)
{
    struct Node *root = NULL;
    int values[] = { 10, 20, 30, 40, 50, 25 };
    int i, n = sizeof(values) / sizeof(values[0]);

    for (i = 0; i < n; i++)
        root = insert(root, values[i]);

    printf("Preorder of AVL tree : "); preorder(root);
    printf("\nInorder (sorted)     : "); inorder(root);
    printf("\nHeight of AVL tree   : %d\n", height(root));
    return 0;
}
/* Preorder = 30 20 10 25 40 50  -> tree stays balanced */

Q5. Write a program in C to implement Huffman coding (Huffman algorithm).

/* Unit VI - Program 5: Huffman coding algorithm (greedy) */
#include <stdio.h>
#include <stdlib.h>
#define MAX 100

struct Node {
    char ch;
    int freq;
    struct Node *left, *right;
};

struct Node *heap[MAX];
int heapSize = 0;

struct Node *createNode(char ch, int freq)
{
    struct Node *n = malloc(sizeof(struct Node));
    n->ch = ch; n->freq = freq;
    n->left = n->right = NULL;
    return n;
}

void insertNode(struct Node *n)
{
    heap[heapSize++] = n;
}

/* remove and return node with minimum frequency */
struct Node *extractMin(void)
{
    int i, minIndex = 0;
    struct Node *minNode;

    for (i = 1; i < heapSize; i++)
        if (heap[i]->freq < heap[minIndex]->freq)
            minIndex = i;

    minNode = heap[minIndex];
    for (i = minIndex; i < heapSize - 1; i++)
        heap[i] = heap[i + 1];
    heapSize--;
    return minNode;
}

struct Node *buildHuffmanTree(char chars[], int freq[], int n)
{
    int i;
    struct Node *left, *right, *parent;

    for (i = 0; i < n; i++)
        insertNode(createNode(chars[i], freq[i]));

    while (heapSize > 1) {
        left  = extractMin();
        right = extractMin();
        parent = createNode('$', left->freq + right->freq);  /* internal node */
        parent->left  = left;
        parent->right = right;
        insertNode(parent);
    }
    return extractMin();
}

void printCodes(struct Node *root, char code[], int top)
{
    if (root == NULL) return;

    if (root->left != NULL) {
        code[top] = '0';
        printCodes(root->left, code, top + 1);
    }
    if (root->right != NULL) {
        code[top] = '1';
        printCodes(root->right, code, top + 1);
    }
    if (root->left == NULL && root->right == NULL) {   /* leaf = character */
        code[top] = '\0';
        printf("  %c   |   %-4d  |  %s\n", root->ch, root->freq, code);
    }
}

int main(void)
{
    char chars[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
    int  freq[]  = {   5,   9,  12,  13,  16,  45 };
    int n = sizeof(chars) / sizeof(chars[0]);
    char code[MAX];
    struct Node *root;

    root = buildHuffmanTree(chars, freq, n);

    printf("Char  |  Freq   |  Huffman Code\n");
    printf("------------------------------\n");
    printCodes(root, code, 0);
    return 0;
}

Q6. Write a program in C to implement a B-Tree with insertion, traversal and searching.

/* Unit VI - Program 6: B-Tree (minimum degree t = 3, i.e. max 5 keys per node) */
#include <stdio.h>
#include <stdlib.h>

#define T 3                     /* minimum degree */

struct BTreeNode {
    int keys[2 * T - 1];        /* max 2t-1 keys       */
    struct BTreeNode *child[2 * T];   /* max 2t children */
    int n;                      /* current key count   */
    int leaf;                   /* 1 if leaf node      */
};

struct BTreeNode *createNode(int leaf)
{
    struct BTreeNode *node = malloc(sizeof(struct BTreeNode));
    int i;
    node->leaf = leaf;
    node->n = 0;
    for (i = 0; i < 2 * T; i++) node->child[i] = NULL;
    return node;
}

/* inorder-like traversal of B-Tree -> gives sorted keys */
void traverse(struct BTreeNode *root)
{
    int i;
    if (root == NULL) return;
    for (i = 0; i < root->n; i++) {
        if (!root->leaf) traverse(root->child[i]);
        printf("%d ", root->keys[i]);
    }
    if (!root->leaf) traverse(root->child[i]);
}

/* search a key */
struct BTreeNode *search(struct BTreeNode *root, int k)
{
    int i = 0;
    if (root == NULL) return NULL;
    while (i < root->n && k > root->keys[i]) i++;
    if (i < root->n && root->keys[i] == k) return root;
    if (root->leaf) return NULL;
    return search(root->child[i], k);
}

/* split the full child y = x->child[i] */
void splitChild(struct BTreeNode *x, int i, struct BTreeNode *y)
{
    struct BTreeNode *z = createNode(y->leaf);
    int j;

    z->n = T - 1;
    for (j = 0; j < T - 1; j++)                 /* copy last t-1 keys to z */
        z->keys[j] = y->keys[j + T];
    if (!y->leaf)
        for (j = 0; j < T; j++)                 /* copy last t children */
            z->child[j] = y->child[j + T];
    y->n = T - 1;

    for (j = x->n; j >= i + 1; j--)             /* make room in x */
        x->child[j + 1] = x->child[j];
    x->child[i + 1] = z;

    for (j = x->n - 1; j >= i; j--)
        x->keys[j + 1] = x->keys[j];
    x->keys[i] = y->keys[T - 1];                /* middle key moves up */
    x->n = x->n + 1;
}

void insertNonFull(struct BTreeNode *x, int k)
{
    int i = x->n - 1;

    if (x->leaf) {
        while (i >= 0 && x->keys[i] > k) {
            x->keys[i + 1] = x->keys[i];
            i--;
        }
        x->keys[i + 1] = k;
        x->n = x->n + 1;
    } else {
        while (i >= 0 && x->keys[i] > k) i--;
        i++;
        if (x->child[i]->n == 2 * T - 1) {      /* child is full -> split */
            splitChild(x, i, x->child[i]);
            if (x->keys[i] < k) i++;
        }
        insertNonFull(x->child[i], k);
    }
}

struct BTreeNode *insert(struct BTreeNode *root, int k)
{
    if (root == NULL) {                         /* empty tree */
        root = createNode(1);
        root->keys[0] = k;
        root->n = 1;
        return root;
    }
    if (root->n == 2 * T - 1) {                 /* root is full -> grow height */
        struct BTreeNode *s = createNode(0);
        int i = 0;
        s->child[0] = root;
        splitChild(s, 0, root);
        if (s->keys[0] < k) i = 1;
        insertNonFull(s->child[i], k);
        return s;                               /* new root */
    }
    insertNonFull(root, k);
    return root;
}

int main(void)
{
    struct BTreeNode *root = NULL;
    int values[] = { 10, 20, 5, 6, 12, 30, 7, 17, 3, 25, 40, 1 };
    int i, n = sizeof(values) / sizeof(values[0]);
    int key = 17;

    for (i = 0; i < n; i++)
        root = insert(root, values[i]);

    printf("B-Tree traversal (sorted order): ");
    traverse(root);
    printf("\n");

    printf("Search %d : %s\n", key, search(root, key) ? "FOUND" : "NOT FOUND");
    printf("Search %d : %s\n", 99,  search(root, 99)  ? "FOUND" : "NOT FOUND");
    printf("Keys in root node: ");
    for (i = 0; i < root->n; i++) printf("%d ", root->keys[i]);
    printf("\n");
    return 0;
}

UNIT VII — Sorting

Q1. Write a program in C to sort an array using Bubble Sort.

/* Unit VII - Program 1: Bubble Sort   Best O(n), Avg/Worst O(n^2), Space O(1), Stable */
#include <stdio.h>

void bubbleSort(int a[], int n)
{
    int i, j, temp, swapped;

    for (i = 0; i < n - 1; i++) {                 /* n-1 passes */
        swapped = 0;
        for (j = 0; j < n - 1 - i; j++) {
            if (a[j] > a[j + 1]) {                /* swap adjacent */
                temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
                swapped = 1;
            }
        }
        printf("Pass %d: ", i + 1);
        for (j = 0; j < n; j++) printf("%d ", a[j]);
        printf("\n");

        if (swapped == 0) break;                  /* already sorted */
    }
}

int main(void)
{
    int a[] = { 64, 34, 25, 12, 22, 11, 90 };
    int n = sizeof(a) / sizeof(a[0]), i;

    printf("Original array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n\n");

    bubbleSort(a, n);

    printf("\nSorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q2. Write a program in C to sort an array using Insertion Sort.

/* Unit VII - Program 2: Insertion Sort   Best O(n), Worst O(n^2), Stable */
#include <stdio.h>

void insertionSort(int a[], int n)
{
    int i, j, key;

    for (i = 1; i < n; i++) {
        key = a[i];                    /* element to be inserted */
        j = i - 1;

        /* shift all larger elements one position right */
        while (j >= 0 && a[j] > key) {
            a[j + 1] = a[j];
            j--;
        }
        a[j + 1] = key;                /* insert at correct place */
    }
}

int main(void)
{
    int a[] = { 12, 11, 13, 5, 6 };
    int n = sizeof(a) / sizeof(a[0]), i;

    insertionSort(a, n);

    printf("Sorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q3. Write a program in C to sort an array using Selection Sort.

/* Unit VII - Program 3: Selection Sort   O(n^2) always, Space O(1), Not stable */
#include <stdio.h>

void selectionSort(int a[], int n)
{
    int i, j, minIndex, temp;

    for (i = 0; i < n - 1; i++) {
        minIndex = i;                              /* assume i is minimum */
        for (j = i + 1; j < n; j++)
            if (a[j] < a[minIndex])
                minIndex = j;                      /* find actual minimum */

        if (minIndex != i) {                       /* swap */
            temp = a[i];
            a[i] = a[minIndex];
            a[minIndex] = temp;
        }
    }
}

int main(void)
{
    int a[] = { 29, 10, 14, 37, 13 };
    int n = sizeof(a) / sizeof(a[0]), i;

    selectionSort(a, n);
    printf("Sorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q4. Write a program in C to sort an array using Quick Sort.

/* Unit VII - Program 4: Quick Sort (divide & conquer)
   Best/Avg O(n log n), Worst O(n^2), Space O(log n)                */
#include <stdio.h>

void swap(int *a, int *b)
{
    int t = *a; *a = *b; *b = t;
}

int partition(int a[], int low, int high)
{
    int pivot = a[high];              /* last element as pivot */
    int i = low - 1, j;

    for (j = low; j < high; j++) {
        if (a[j] <= pivot) {
            i++;
            swap(&a[i], &a[j]);
        }
    }
    swap(&a[i + 1], &a[high]);        /* pivot to its final place */
    return i + 1;
}

void quickSort(int a[], int low, int high)
{
    int pi;
    if (low < high) {
        pi = partition(a, low, high);         /* DIVIDE */
        quickSort(a, low, pi - 1);            /* CONQUER left  */
        quickSort(a, pi + 1, high);           /* CONQUER right */
    }
}

int main(void)
{
    int a[] = { 10, 7, 8, 9, 1, 5 };
    int n = sizeof(a) / sizeof(a[0]), i;

    quickSort(a, 0, n - 1);
    printf("Sorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q5. Write a program in C to sort an array using Merge Sort.

/* Unit VII - Program 5: Merge Sort (divide & conquer)
   Time O(n log n) in all cases, Space O(n), Stable                  */
#include <stdio.h>
#define MAX 100

void merge(int a[], int low, int mid, int high)
{
    int temp[MAX];
    int i = low, j = mid + 1, k = low;

    while (i <= mid && j <= high) {
        if (a[i] <= a[j]) temp[k++] = a[i++];
        else              temp[k++] = a[j++];
    }
    while (i <= mid)  temp[k++] = a[i++];        /* copy remaining left  */
    while (j <= high) temp[k++] = a[j++];        /* copy remaining right */

    for (i = low; i <= high; i++)
        a[i] = temp[i];
}

void mergeSort(int a[], int low, int high)
{
    int mid;
    if (low < high) {
        mid = (low + high) / 2;
        mergeSort(a, low, mid);            /* sort left half  */
        mergeSort(a, mid + 1, high);       /* sort right half */
        merge(a, low, mid, high);          /* merge them      */
    }
}

int main(void)
{
    int a[] = { 38, 27, 43, 3, 9, 82, 10 };
    int n = sizeof(a) / sizeof(a[0]), i;

    mergeSort(a, 0, n - 1);
    printf("Sorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q6. Write a program in C to sort an array using Shell Sort.

/* Unit VII - Program 6: Shell Sort (diminishing increment sort)
   Time O(n log n) to O(n^2) depending on gap sequence               */
#include <stdio.h>

void shellSort(int a[], int n)
{
    int gap, i, j, temp;

    for (gap = n / 2; gap > 0; gap /= 2) {        /* reduce gap each pass */
        for (i = gap; i < n; i++) {
            temp = a[i];
            /* gapped insertion sort */
            for (j = i; j >= gap && a[j - gap] > temp; j -= gap)
                a[j] = a[j - gap];
            a[j] = temp;
        }
        printf("After gap = %d : ", gap);
        for (j = 0; j < n; j++) printf("%d ", a[j]);
        printf("\n");
    }
}

int main(void)
{
    int a[] = { 12, 34, 54, 2, 3, 45, 8, 21 };
    int n = sizeof(a) / sizeof(a[0]), i;

    shellSort(a, n);
    printf("\nSorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q7. Write a program in C to sort an array using Binary Insertion Sort (Binary Sort).

/* Unit VII - Program 7: Binary Sort = Binary Insertion Sort
   Uses binary search to find the insertion position -> fewer comparisons */
#include <stdio.h>

/* returns the position where 'key' should be inserted in a[low..high] */
int binarySearchPos(int a[], int key, int low, int high)
{
    int mid;
    if (high <= low)
        return (key > a[low]) ? (low + 1) : low;

    mid = (low + high) / 2;
    if (key == a[mid])  return mid + 1;
    if (key > a[mid])   return binarySearchPos(a, key, mid + 1, high);
    return binarySearchPos(a, key, low, mid - 1);
}

void binaryInsertionSort(int a[], int n)
{
    int i, j, pos, key;

    for (i = 1; i < n; i++) {
        key = a[i];
        j   = i - 1;
        pos = binarySearchPos(a, key, 0, j);      /* find position */

        while (j >= pos) {                        /* shift right */
            a[j + 1] = a[j];
            j--;
        }
        a[pos] = key;
    }
}

int main(void)
{
    int a[] = { 37, 23, 0, 17, 12, 72, 31, 46 };
    int n = sizeof(a) / sizeof(a[0]), i;

    binaryInsertionSort(a, n);
    printf("Sorted array: ");
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

Q8. Write a menu-driven program in C that sorts an array using any of the sorting techniques and compares the number of comparisons (efficiency of sorting).

/* Unit VII - Program 8: All sorting techniques in one menu-driven program
   + comparison counter (efficiency / Big-O demonstration)             */
#include <stdio.h>
#include <stdlib.h>
#define MAX 100

long comparisons;

void printArray(int a[], int n)
{
    int i;
    for (i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\n");
}

void copyArray(int src[], int dest[], int n)
{
    int i;
    for (i = 0; i < n; i++) dest[i] = src[i];
}

void bubbleSort(int a[], int n)
{
    int i, j, t;
    for (i = 0; i < n - 1; i++)
        for (j = 0; j < n - 1 - i; j++) {
            comparisons++;
            if (a[j] > a[j + 1]) { t = a[j]; a[j] = a[j + 1]; a[j + 1] = t; }
        }
}

void insertionSort(int a[], int n)
{
    int i, j, key;
    for (i = 1; i < n; i++) {
        key = a[i]; j = i - 1;
        while (j >= 0 && (++comparisons) && a[j] > key) { a[j + 1] = a[j]; j--; }
        a[j + 1] = key;
    }
}

void selectionSort(int a[], int n)
{
    int i, j, min, t;
    for (i = 0; i < n - 1; i++) {
        min = i;
        for (j = i + 1; j < n; j++) { comparisons++; if (a[j] < a[min]) min = j; }
        t = a[i]; a[i] = a[min]; a[min] = t;
    }
}

int partition(int a[], int low, int high)
{
    int pivot = a[high], i = low - 1, j, t;
    for (j = low; j < high; j++) {
        comparisons++;
        if (a[j] <= pivot) { i++; t = a[i]; a[i] = a[j]; a[j] = t; }
    }
    t = a[i + 1]; a[i + 1] = a[high]; a[high] = t;
    return i + 1;
}

void quickSort(int a[], int low, int high)
{
    int pi;
    if (low < high) {
        pi = partition(a, low, high);
        quickSort(a, low, pi - 1);
        quickSort(a, pi + 1, high);
    }
}

void merge(int a[], int low, int mid, int high)
{
    int temp[MAX], i = low, j = mid + 1, k = low;
    while (i <= mid && j <= high) {
        comparisons++;
        if (a[i] <= a[j]) temp[k++] = a[i++]; else temp[k++] = a[j++];
    }
    while (i <= mid)  temp[k++] = a[i++];
    while (j <= high) temp[k++] = a[j++];
    for (i = low; i <= high; i++) a[i] = temp[i];
}

void mergeSort(int a[], int low, int high)
{
    int mid;
    if (low < high) {
        mid = (low + high) / 2;
        mergeSort(a, low, mid);
        mergeSort(a, mid + 1, high);
        merge(a, low, mid, high);
    }
}

void shellSort(int a[], int n)
{
    int gap, i, j, temp;
    for (gap = n / 2; gap > 0; gap /= 2)
        for (i = gap; i < n; i++) {
            temp = a[i];
            for (j = i; j >= gap && (++comparisons) && a[j - gap] > temp; j -= gap)
                a[j] = a[j - gap];
            a[j] = temp;
        }
}

int main(void)
{
    int original[MAX], work[MAX], n, i, choice;

    printf("Enter number of elements: ");
    scanf("%d", &n);
    printf("Enter %d elements: ", n);
    for (i = 0; i < n; i++) scanf("%d", &original[i]);

    while (1) {
        printf("\n1.Bubble 2.Insertion 3.Selection 4.Quick 5.Merge 6.Shell 7.Exit\n");
        printf("Enter choice: ");
        if (scanf("%d", &choice) != 1) break;

        copyArray(original, work, n);
        comparisons = 0;

        switch (choice) {
        case 1: bubbleSort(work, n);        printf("Bubble Sort   O(n^2)     : "); break;
        case 2: insertionSort(work, n);     printf("Insertion Sort O(n^2)    : "); break;
        case 3: selectionSort(work, n);     printf("Selection Sort O(n^2)    : "); break;
        case 4: quickSort(work, 0, n - 1);  printf("Quick Sort O(n log n)    : "); break;
        case 5: mergeSort(work, 0, n - 1);  printf("Merge Sort O(n log n)    : "); break;
        case 6: shellSort(work, n);         printf("Shell Sort O(n log n)    : "); break;
        case 7: exit(0);
        default: printf("Invalid choice\n"); continue;
        }
        printArray(work, n);
        printf("Comparisons made = %ld\n", comparisons);
    }
    return 0;
}

UNIT VIII — Searching & Hashing

Q1. Write a program in C to search an element using Sequential (Linear) Search.

/* Unit VIII - Program 1: Sequential / Linear search  O(n) */
#include <stdio.h>

int linearSearch(int a[], int n, int key)
{
    int i;
    for (i = 0; i < n; i++)
        if (a[i] == key)
            return i;                 /* found -> return index */
    return -1;                        /* not found */
}

int main(void)
{
    int a[100], n, i, key, pos;

    printf("Enter number of elements: ");
    scanf("%d", &n);
    printf("Enter %d elements: ", n);
    for (i = 0; i < n; i++) scanf("%d", &a[i]);

    printf("Enter element to search: ");
    scanf("%d", &key);

    pos = linearSearch(a, n, key);
    if (pos == -1) printf("%d not found in the array\n", key);
    else           printf("%d found at index %d (position %d)\n", key, pos, pos + 1);
    return 0;
}

Q2. Write a program in C to search an element using Binary Search (iterative and recursive).

/* Unit VIII - Program 2: Binary search - iterative and recursive  O(log n)
   NOTE: array must be sorted                                            */
#include <stdio.h>

int binarySearchIterative(int a[], int n, int key)
{
    int low = 0, high = n - 1, mid;

    while (low <= high) {
        mid = low + (high - low) / 2;
        if (a[mid] == key)      return mid;
        else if (a[mid] < key)  low  = mid + 1;   /* search right half */
        else                    high = mid - 1;   /* search left half  */
    }
    return -1;
}

int binarySearchRecursive(int a[], int low, int high, int key)
{
    int mid;
    if (low > high) return -1;                    /* base case */

    mid = low + (high - low) / 2;
    if (a[mid] == key)     return mid;
    if (a[mid] < key)      return binarySearchRecursive(a, mid + 1, high, key);
    return binarySearchRecursive(a, low, mid - 1, key);
}

int main(void)
{
    int a[] = { 11, 22, 33, 44, 55, 66, 77, 88, 99 };
    int n = sizeof(a) / sizeof(a[0]);
    int key, p1, p2;

    printf("Sorted array: ");
    { int i; for (i = 0; i < n; i++) printf("%d ", a[i]); }

    printf("\nEnter element to search: ");
    scanf("%d", &key);

    p1 = binarySearchIterative(a, n, key);
    p2 = binarySearchRecursive(a, 0, n - 1, key);

    printf("Iterative: %s", p1 == -1 ? "Not found\n" : "");
    if (p1 != -1) printf("found at index %d\n", p1);
    printf("Recursive: %s", p2 == -1 ? "Not found\n" : "");
    if (p2 != -1) printf("found at index %d\n", p2);
    return 0;
}

Q3. Write a program in C to implement hashing with Linear Probing (open addressing).

/* Unit VIII - Program 3: Hashing with LINEAR PROBING
   h(k) = k mod SIZE ,  on collision:  (h(k) + i) mod SIZE            */
#include <stdio.h>
#define SIZE 10
#define EMPTY -1

int hashTable[SIZE];

void initTable(void)
{
    int i;
    for (i = 0; i < SIZE; i++) hashTable[i] = EMPTY;
}

int hashFunction(int key)
{
    return key % SIZE;                 /* division method */
}

void insert(int key)
{
    int index = hashFunction(key);
    int i = 0;

    while (hashTable[(index + i) % SIZE] != EMPTY) {
        i++;
        if (i == SIZE) { printf("Hash table is FULL, cannot insert %d\n", key); return; }
    }
    hashTable[(index + i) % SIZE] = key;
    printf("Inserted %d at index %d (home=%d, probes=%d)\n",
           key, (index + i) % SIZE, index, i);
}

int search(int key)
{
    int index = hashFunction(key);
    int i = 0;

    while (hashTable[(index + i) % SIZE] != EMPTY) {
        if (hashTable[(index + i) % SIZE] == key)
            return (index + i) % SIZE;
        i++;
        if (i == SIZE) break;
    }
    return -1;
}

void display(void)
{
    int i;
    printf("\nHash Table:\n");
    for (i = 0; i < SIZE; i++) {
        printf("[%d] -> ", i);
        if (hashTable[i] == EMPTY) printf("empty\n");
        else                       printf("%d\n", hashTable[i]);
    }
}

int main(void)
{
    int keys[] = { 12, 22, 32, 45, 67, 15, 25 };
    int n = sizeof(keys) / sizeof(keys[0]), i, pos;

    initTable();
    for (i = 0; i < n; i++) insert(keys[i]);
    display();

    pos = search(32);
    printf("\nSearch 32 : %s", pos == -1 ? "not found\n" : "");
    if (pos != -1) printf("found at index %d\n", pos);
    return 0;
}

Q4. Write a program in C to implement hashing with Quadratic Probing and Double Hashing.

/* Unit VIII - Program 4: Quadratic Probing and Double Hashing
   Quadratic : (h(k) + i*i) mod SIZE
   Double    : (h1(k) + i*h2(k)) mod SIZE  where h2(k) = 1 + (k mod (SIZE-1)) */
#include <stdio.h>
#define SIZE 11              /* prime size works best */
#define EMPTY -1

int qTable[SIZE], dTable[SIZE];

void initTables(void)
{
    int i;
    for (i = 0; i < SIZE; i++) { qTable[i] = EMPTY; dTable[i] = EMPTY; }
}

int h1(int key) { return key % SIZE; }
int h2(int key) { return 1 + (key % (SIZE - 1)); }   /* must never be 0 */

void insertQuadratic(int key)
{
    int home = h1(key), i, index;
    for (i = 0; i < SIZE; i++) {
        index = (home + i * i) % SIZE;
        if (qTable[index] == EMPTY) {
            qTable[index] = key;
            printf("Quadratic : %3d -> index %2d (home %2d, i=%d)\n", key, index, home, i);
            return;
        }
    }
    printf("Quadratic : could not insert %d\n", key);
}

void insertDouble(int key)
{
    int home = h1(key), step = h2(key), i, index;
    for (i = 0; i < SIZE; i++) {
        index = (home + i * step) % SIZE;
        if (dTable[index] == EMPTY) {
            dTable[index] = key;
            printf("Double    : %3d -> index %2d (h1=%2d, h2=%d, i=%d)\n",
                   key, index, home, step, i);
            return;
        }
    }
    printf("Double    : could not insert %d\n", key);
}

void display(int t[], const char *name)
{
    int i;
    printf("\n%s table:\n", name);
    for (i = 0; i < SIZE; i++) {
        printf("[%2d] ", i);
        if (t[i] == EMPTY) printf("empty\n"); else printf("%d\n", t[i]);
    }
}

int main(void)
{
    int keys[] = { 22, 33, 44, 11, 55, 66 };
    int n = sizeof(keys) / sizeof(keys[0]), i;

    initTables();
    for (i = 0; i < n; i++) insertQuadratic(keys[i]);
    printf("\n");
    for (i = 0; i < n; i++) insertDouble(keys[i]);

    display(qTable, "Quadratic probing");
    display(dTable, "Double hashing");
    return 0;
}

Q5. Write a program in C to implement hashing with Chaining (collision resolution by separate chaining).

/* Unit VIII - Program 5: Hashing with CHAINING (each slot holds a linked list) */
#include <stdio.h>
#include <stdlib.h>
#define SIZE 7

struct Node {
    int key;
    struct Node *next;
};

struct Node *hashTable[SIZE] = { NULL };

int hashFunction(int key) { return key % SIZE; }

void insert(int key)
{
    int index = hashFunction(key);
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->key  = key;
    newNode->next = hashTable[index];      /* insert at front of chain */
    hashTable[index] = newNode;
    printf("Inserted %d into chain %d\n", key, index);
}

int search(int key)
{
    int index = hashFunction(key);
    struct Node *temp = hashTable[index];
    int steps = 0;

    while (temp != NULL) {
        steps++;
        if (temp->key == key) {
            printf("%d found in chain %d after %d comparison(s)\n", key, index, steps);
            return 1;
        }
        temp = temp->next;
    }
    printf("%d not found\n", key);
    return 0;
}

void deleteKey(int key)
{
    int index = hashFunction(key);
    struct Node *temp = hashTable[index], *prev = NULL;

    while (temp != NULL && temp->key != key) { prev = temp; temp = temp->next; }
    if (temp == NULL) { printf("%d not found, cannot delete\n", key); return; }

    if (prev == NULL) hashTable[index] = temp->next;
    else              prev->next = temp->next;
    free(temp);
    printf("Deleted %d\n", key);
}

void display(void)
{
    int i;
    struct Node *temp;
    printf("\nHash table with chaining:\n");
    for (i = 0; i < SIZE; i++) {
        printf("[%d]", i);
        temp = hashTable[i];
        while (temp != NULL) { printf(" -> %d", temp->key); temp = temp->next; }
        printf(" -> NULL\n");
    }
}

int main(void)
{
    int keys[] = { 10, 20, 15, 7, 24, 3, 17 };
    int n = sizeof(keys) / sizeof(keys[0]), i;

    for (i = 0; i < n; i++) insert(keys[i]);
    display();
    search(24);
    search(99);
    deleteKey(15);
    display();
    return 0;
}

Q6. Write a program in C to demonstrate Rehashing (when the load factor exceeds a threshold).

/* Unit VIII - Program 6: REHASHING
   When load factor = n/size > 0.75, table size is doubled (next prime)
   and every key is re-inserted with the new hash function.              */
#include <stdio.h>
#include <stdlib.h>
#define EMPTY -1

int *table;
int tableSize = 7;
int count = 0;

int isPrime(int n)
{
    int i;
    if (n < 2) return 0;
    for (i = 2; i * i <= n; i++)
        if (n % i == 0) return 0;
    return 1;
}

int nextPrime(int n)
{
    while (!isPrime(n)) n++;
    return n;
}

void initTable(int size)
{
    int i;
    table = malloc(sizeof(int) * size);
    for (i = 0; i < size; i++) table[i] = EMPTY;
}

void insertKey(int key);          /* forward declaration */

void rehash(void)
{
    int *oldTable = table;
    int oldSize = tableSize, i;

    tableSize = nextPrime(oldSize * 2);
    printf(">>> REHASHING: size %d -> %d\n", oldSize, tableSize);

    initTable(tableSize);
    count = 0;

    for (i = 0; i < oldSize; i++)              /* re-insert all old keys */
        if (oldTable[i] != EMPTY)
            insertKey(oldTable[i]);

    free(oldTable);
}

void insertKey(int key)
{
    int index, i = 0;

    index = key % tableSize;
    while (table[(index + i) % tableSize] != EMPTY) i++;   /* linear probing */
    table[(index + i) % tableSize] = key;
    count++;

    if ((double) count / tableSize > 0.75)                 /* load factor check */
        rehash();
}

void display(void)
{
    int i;
    printf("Table (size=%d, keys=%d, load factor=%.2f)\n",
           tableSize, count, (double) count / tableSize);
    for (i = 0; i < tableSize; i++) {
        printf("[%2d] ", i);
        if (table[i] == EMPTY) printf("empty\n"); else printf("%d\n", table[i]);
    }
    printf("\n");
}

int main(void)
{
    int keys[] = { 10, 20, 30, 40, 50, 60, 70, 80 };
    int n = sizeof(keys) / sizeof(keys[0]), i;

    initTable(tableSize);
    for (i = 0; i < n; i++) {
        printf("Inserting %d\n", keys[i]);
        insertKey(keys[i]);
    }
    display();
    return 0;
}

UNIT IX — Graph

Q1. Write a program in C to represent a graph using an Adjacency Matrix and an Adjacency List.

/* Unit IX - Program 1: Graph representation - adjacency matrix + adjacency list */
#include <stdio.h>
#include <stdlib.h>
#define MAX 20

int adj[MAX][MAX];          /* adjacency matrix */
int n;                      /* number of vertices */

struct Node {               /* node of adjacency list */
    int vertex;
    struct Node *next;
};
struct Node *list[MAX];

void addEdgeList(int u, int v)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->vertex = v;
    newNode->next = list[u];
    list[u] = newNode;

    newNode = malloc(sizeof(struct Node));       /* undirected -> both ways */
    newNode->vertex = u;
    newNode->next = list[v];
    list[v] = newNode;
}

void displayMatrix(void)
{
    int i, j;
    printf("\nAdjacency Matrix:\n    ");
    for (i = 0; i < n; i++) printf("%3d", i);
    printf("\n");
    for (i = 0; i < n; i++) {
        printf("%3d ", i);
        for (j = 0; j < n; j++) printf("%3d", adj[i][j]);
        printf("\n");
    }
}

void displayList(void)
{
    int i;
    struct Node *temp;
    printf("\nAdjacency List:\n");
    for (i = 0; i < n; i++) {
        printf("%d", i);
        temp = list[i];
        while (temp != NULL) { printf(" -> %d", temp->vertex); temp = temp->next; }
        printf("\n");
    }
}

int main(void)
{
    int e, i, u, v;

    printf("Enter number of vertices and edges: ");
    scanf("%d %d", &n, &e);

    for (i = 0; i < n; i++) list[i] = NULL;

    printf("Enter %d edges (u v):\n", e);
    for (i = 0; i < e; i++) {
        scanf("%d %d", &u, &v);
        adj[u][v] = 1;
        adj[v][u] = 1;                /* remove this line for directed graph */
        addEdgeList(u, v);
    }
    displayMatrix();
    displayList();
    return 0;
}

Q2. Write a program in C to traverse a graph using Breadth First Search (BFS).

/* Unit IX - Program 2: BFS traversal using a queue   O(V^2) with matrix */
#include <stdio.h>
#define MAX 20

int adj[MAX][MAX], visited[MAX], n;
int queue[MAX], front = 0, rear = 0;

void bfs(int start)
{
    int current, i;

    visited[start] = 1;
    queue[rear++] = start;

    printf("BFS traversal: ");
    while (front < rear) {
        current = queue[front++];         /* dequeue */
        printf("%d ", current);

        for (i = 0; i < n; i++) {
            if (adj[current][i] == 1 && visited[i] == 0) {
                visited[i] = 1;
                queue[rear++] = i;        /* enqueue unvisited neighbour */
            }
        }
    }
    printf("\n");
}

int main(void)
{
    int i, j, start;

    printf("Enter number of vertices: ");
    scanf("%d", &n);

    printf("Enter adjacency matrix (%dx%d):\n", n, n);
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            scanf("%d", &adj[i][j]);

    for (i = 0; i < n; i++) visited[i] = 0;

    printf("Enter starting vertex: ");
    scanf("%d", &start);

    bfs(start);
    return 0;
}
/* Sample input:
   4
   0 1 1 0
   1 0 0 1
   1 0 0 1
   0 1 1 0
   0            ->  BFS: 0 1 2 3                                    */

Q3. Write a program in C to traverse a graph using Depth First Search (DFS) — recursive and using a stack.

/* Unit IX - Program 3: DFS traversal - recursive and iterative (stack) */
#include <stdio.h>
#define MAX 20

int adj[MAX][MAX], visited[MAX], n;

void dfsRecursive(int v)
{
    int i;
    visited[v] = 1;
    printf("%d ", v);

    for (i = 0; i < n; i++)
        if (adj[v][i] == 1 && !visited[i])
            dfsRecursive(i);
}

void dfsIterative(int start)
{
    int stack[MAX], top = -1, current, i;

    stack[++top] = start;

    while (top != -1) {
        current = stack[top--];               /* pop */
        if (!visited[current]) {
            visited[current] = 1;
            printf("%d ", current);
        }
        /* push neighbours in reverse so smallest is processed first */
        for (i = n - 1; i >= 0; i--)
            if (adj[current][i] == 1 && !visited[i])
                stack[++top] = i;
    }
}

int main(void)
{
    int i, j, start;

    printf("Enter number of vertices: ");
    scanf("%d", &n);
    printf("Enter adjacency matrix:\n");
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            scanf("%d", &adj[i][j]);
    printf("Enter starting vertex: ");
    scanf("%d", &start);

    for (i = 0; i < n; i++) visited[i] = 0;
    printf("DFS (recursive): ");
    dfsRecursive(start);

    for (i = 0; i < n; i++) visited[i] = 0;
    printf("\nDFS (iterative): ");
    dfsIterative(start);
    printf("\n");
    return 0;
}

Q4. Write a program in C to find the Minimum Spanning Tree using Prim's Algorithm.

/* Unit IX - Program 4: Prim's algorithm for Minimum Spanning Tree  O(V^2) */
#include <stdio.h>
#define MAX 20
#define INF 9999

int cost[MAX][MAX], n;

void prim(void)
{
    int visited[MAX], i, j, u = 0, v = 0, edgeCount = 0, minCost = 0, min;

    for (i = 0; i < n; i++) visited[i] = 0;
    visited[0] = 1;                        /* start from vertex 0 */

    printf("Edges of Minimum Spanning Tree:\n");

    while (edgeCount < n - 1) {
        min = INF;
        /* find minimum cost edge from visited set to unvisited set */
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                if (visited[i] == 1 && visited[j] == 0 && cost[i][j] < min) {
                    min = cost[i][j];
                    u = i;
                    v = j;
                }

        printf("  %d - %d  weight = %d\n", u, v, min);
        visited[v] = 1;
        minCost += min;
        edgeCount++;
    }
    printf("Total minimum cost = %d\n", minCost);
}

int main(void)
{
    int i, j;

    printf("Enter number of vertices: ");
    scanf("%d", &n);
    printf("Enter cost matrix (0 or 9999 for no edge):\n");
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++) {
            scanf("%d", &cost[i][j]);
            if (cost[i][j] == 0) cost[i][j] = INF;    /* no edge */
        }

    prim();
    return 0;
}
/* Sample input (4 vertices):
   4
   0 10 6 5
   10 0 0 15
   6 0 0 4
   5 15 4 0                                                          */

Q5. Write a program in C to find the Minimum Spanning Tree using Kruskal's Algorithm.

/* Unit IX - Program 5: Kruskal's algorithm using Union-Find  O(E log E) */
#include <stdio.h>
#include <stdlib.h>
#define MAX 50

struct Edge {
    int u, v, weight;
};

struct Edge edges[MAX];
int parent[MAX];
int n, e;

int find(int i)                        /* find set of element i */
{
    while (parent[i] != i)
        i = parent[i];
    return i;
}

void unionSet(int a, int b)
{
    parent[find(a)] = find(b);
}

/* sort edges in ascending order of weight (simple bubble sort) */
void sortEdges(void)
{
    int i, j;
    struct Edge temp;
    for (i = 0; i < e - 1; i++)
        for (j = 0; j < e - 1 - i; j++)
            if (edges[j].weight > edges[j + 1].weight) {
                temp = edges[j];
                edges[j] = edges[j + 1];
                edges[j + 1] = temp;
            }
}

void kruskal(void)
{
    int i, count = 0, totalCost = 0, setU, setV;

    for (i = 0; i < n; i++) parent[i] = i;     /* each vertex is its own set */

    sortEdges();

    printf("Edges of Minimum Spanning Tree:\n");
    for (i = 0; i < e && count < n - 1; i++) {
        setU = find(edges[i].u);
        setV = find(edges[i].v);

        if (setU != setV) {                    /* no cycle formed */
            printf("  %d - %d  weight = %d\n", edges[i].u, edges[i].v, edges[i].weight);
            unionSet(setU, setV);
            totalCost += edges[i].weight;
            count++;
        }
    }
    printf("Total minimum cost = %d\n", totalCost);
}

int main(void)
{
    int i;

    printf("Enter number of vertices and edges: ");
    scanf("%d %d", &n, &e);
    printf("Enter each edge as: u v weight\n");
    for (i = 0; i < e; i++)
        scanf("%d %d %d", &edges[i].u, &edges[i].v, &edges[i].weight);

    kruskal();
    return 0;
}
/* Sample input:
   4 5
   0 1 10
   0 2 6
   0 3 5
   1 3 15
   2 3 4                                                             */

Q6. Write a program in C to find the shortest path from a source vertex using Dijkstra's Algorithm.

/* Unit IX - Program 6: Dijkstra's shortest path algorithm  O(V^2) */
#include <stdio.h>
#define MAX 20
#define INF 9999

int cost[MAX][MAX], n;

void dijkstra(int source)
{
    int distance[MAX], visited[MAX], parent[MAX];
    int i, j, u = 0, min, count;

    for (i = 0; i < n; i++) {
        distance[i] = cost[source][i];
        visited[i]  = 0;
        parent[i]   = source;
    }
    distance[source] = 0;
    visited[source]  = 1;
    count = 1;

    while (count < n) {
        min = INF;
        for (i = 0; i < n; i++)                     /* pick nearest unvisited */
            if (!visited[i] && distance[i] < min) { min = distance[i]; u = i; }

        visited[u] = 1;
        count++;

        for (j = 0; j < n; j++)                     /* relax edges */
            if (!visited[j] && distance[u] + cost[u][j] < distance[j]) {
                distance[j] = distance[u] + cost[u][j];
                parent[j] = u;
            }
    }

    printf("\nShortest distances from vertex %d:\n", source);
    for (i = 0; i < n; i++) {
        if (i == source) continue;
        printf("  To %d : distance = %d, path = %d", i, distance[i], i);
        j = i;
        while (j != source) { j = parent[j]; printf(" <- %d", j); }
        printf("\n");
    }
}

int main(void)
{
    int i, j, source;

    printf("Enter number of vertices: ");
    scanf("%d", &n);
    printf("Enter cost matrix (0 for no edge):\n");
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++) {
            scanf("%d", &cost[i][j]);
            if (cost[i][j] == 0 && i != j) cost[i][j] = INF;
        }

    printf("Enter source vertex: ");
    scanf("%d", &source);
    dijkstra(source);
    return 0;
}
/* Sample input (5 vertices):
   5
   0 10 0 30 100
   10 0 50 0 0
   0 50 0 20 10
   30 0 20 0 60
   100 0 10 60 0
   0                                                                 */

UNIT X — Growth Functions

Q1. Write a program in C to compare the growth rates of common complexity functions (Big O demonstration).

/* Unit X - Program 1: Growth rate comparison table
   compile with: gcc prog.c -o prog -lm                                */
#include <stdio.h>
#include <math.h>

int main(void)
{
    int nValues[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 1024 };
    int count = sizeof(nValues) / sizeof(nValues[0]);
    int i, n;

    printf("%6s %8s %10s %12s %12s %16s\n",
           "n", "log n", "n", "n log n", "n^2", "2^n");
    printf("--------------------------------------------------------------------\n");

    for (i = 0; i < count; i++) {
        n = nValues[i];
        printf("%6d %8.2f %10d %12.2f %12.0f ", n, log2((double) n), n,
               n * log2((double) n), pow((double) n, 2));

        if (n <= 40) printf("%16.0f\n", pow(2.0, (double) n));
        else         printf("%16s\n", "too large");
    }

    printf("\nGrowth order (slowest to fastest):\n");
    printf("O(1) < O(log n) < O(n) < O(n log n) < O(n^2) < O(n^3) < O(2^n) < O(n!)\n");
    return 0;
}

Q2. Write a program in C that counts the basic operations of different algorithms to verify their Big O, Omega and Theta complexity.

/* Unit X - Program 2: Verifying Big O (worst), Omega (best) and Theta (average)
   using operation counting on Linear Search and Bubble Sort            */
#include <stdio.h>

long steps;

/* Linear search: Omega(1) best, O(n) worst, Theta(n) average */
int linearSearch(int a[], int n, int key)
{
    int i;
    steps = 0;
    for (i = 0; i < n; i++) {
        steps++;
        if (a[i] == key) return i;
    }
    return -1;
}

/* Bubble sort: Omega(n) best (sorted), O(n^2) worst (reverse sorted) */
void bubbleSort(int a[], int n)
{
    int i, j, t, swapped;
    steps = 0;
    for (i = 0; i < n - 1; i++) {
        swapped = 0;
        for (j = 0; j < n - 1 - i; j++) {
            steps++;
            if (a[j] > a[j + 1]) {
                t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
                swapped = 1;
            }
        }
        if (!swapped) break;
    }
}

int main(void)
{
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };       /* already sorted */
    int b[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };       /* reverse sorted */
    int n = 10;

    printf("=== LINEAR SEARCH (n = %d) ===\n", n);
    linearSearch(a, n, 1);
    printf("Best case  (first element)  : steps = %ld  -> Omega(1)\n", steps);
    linearSearch(a, n, 10);
    printf("Worst case (last element)   : steps = %ld  -> O(n)\n", steps);
    linearSearch(a, n, 99);
    printf("Not found                   : steps = %ld  -> O(n)\n", steps);

    printf("\n=== BUBBLE SORT (n = %d) ===\n", n);
    bubbleSort(a, n);
    printf("Best case  (sorted input)   : steps = %ld  -> Omega(n)\n", steps);
    bubbleSort(b, n);
    printf("Worst case (reverse input)  : steps = %ld  -> O(n^2)\n", steps);

    printf("\nLimitation of Big O: it gives only an upper bound, it hides\n");
    printf("constants and lower order terms, and it says nothing about the\n");
    printf("best or average case (Omega and Theta are needed for those).\n");
    return 0;
}

Quick Revision — Complexity Table

Algorithm Best Average Worst Space Stable
Bubble Sort O(n) O(n²) O(n²) O(1) Yes
Insertion Sort O(n) O(n²) O(n²) O(1) Yes
Selection Sort O(n²) O(n²) O(n²) O(1) No
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes
Shell Sort O(n log n) O(n^1.25) O(n²) O(1) No
Linear Search O(1) O(n) O(n) O(1) —
Binary Search O(1) O(log n) O(log n) O(1) —
BST operations O(log n) O(log n) O(n) O(n) —
AVL operations O(log n) O(log n) O(log n) O(n) —
Hashing O(1) O(1) O(n) O(n) —
BFS / DFS — O(V+E) O(V²) matrix O(V) —
Prim's — O(V²) O(V²) O(V) —
Kruskal's — O(E log E) O(E log E) O(V) —
Dijkstra's — O(V²) O(V²) O(V) —

Compile & run any program:

gcc program.c -o program
./program

Add -lm for the programs that use math.h (postfix evaluation, growth functions).