Loading...
Development

With C Code, Diagrams, Examples, and Problem Solving

Complete Notes on Stacks and Queues

With C Code, Diagrams, Examples, and Problem Solving


1. STACK – Abstract Data Type (ADT)

Stack is a linear data structure that follows LIFO (Last In, First Out) principle.

Analogy

A stack of plates: You push a plate on top, pop from the top.

    [Plate 3] ← Top
    [Plate 2]
    [Plate 1]

Primitive Operations

OperationDescriptionTime
push(x)Insert x at topO(1)
pop()Remove and return topO(1)
peek() / top()View top elementO(1)
isEmpty()Check if emptyO(1)
isFull()Check if full (array)O(1)

2. Array Implementation of Stack

#include <stdio.h>
#define MAX 100

typedef struct {
    int arr[MAX];
    int top;
} Stack;

void init(Stack* s) {
    s->top = -1;
}

int isEmpty(Stack* s) {
    return s->top == -1;
}

int isFull(Stack* s) {
    return s->top == MAX - 1;
}

void push(Stack* s, int x) {
    if (isFull(s)) {
        printf("Stack Overflow!\n");
        return;
    }
    s->arr[++s->top] = x;
}

int pop(Stack* s) {
    if (isEmpty(s)) {
        printf("Stack Underflow!\n");
        return -1;
    }
    return s->arr[s->top--];
}

int peek(Stack* s) {
    if (isEmpty(s)) return -1;
    return s->arr[s->top];
}

3. Linked List Implementation of Stack

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

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

typedef struct {
    Node* top;
} Stack;

void init(Stack* s) {
    s->top = NULL;
}

void push(Stack* s, int x) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = x;
    newNode->next = s->top;
    s->top = newNode;
}

int pop(Stack* s) {
    if (s->top == NULL) return -1;
    Node* temp = s->top;
    int x = temp->data;
    s->top = s->top->next;
    free(temp);
    return x;
}

4. Applications of Stack

A. Expression Evaluation

TypeExample
InfixA + B * C
Prefix (Polish)+ A * B C
Postfix (RPN)A B C * +

Postfix Evaluation (Using Stack)

Algorithm:

  1. Scan expression left to right
  2. If operandpush
  3. If operatorpop two operands, apply, push result
  4. Final result = top of stack

Example: 2 3 1 * + 9 -

TokenStack
2[2]
3[2,3]
1[2,3,1]
*[2,3] → 3*1=3 → [2,3]
+[2,3] → 2+3=5 → [5]
9[5,9]
-[5,9] → 5-9=-4 → [-4]

Result: -4


C Code: Postfix Evaluation

#include <stdio.h>
#include <ctype.h>
#include "stack.h"  // Use array or linked stack

int evaluatePostfix(char* exp) {
    Stack s;
    init(&s);

    for (int i = 0; exp[i]; i++) {
        if (isdigit(exp[i]))
            push(&s, exp[i] - '0');
        else {
            int val1 = pop(&s);
            int val2 = pop(&s);
            switch (exp[i]) {
                case '+': push(&s, val2 + val1); break;
                case '-': push(&s, val2 - val1); break;
                case '*': push(&s, val2 * val1); break;
                case '/': push(&s, val2 / val1); break;
            }
        }
    }
    return pop(&s);
}

5. Recursion vs Iteration

FeatureRecursionIteration
MechanismFunction calls itselfLoops
MemoryStack (call stack)Constant
SpeedSlower (function call overhead)Faster
ReadabilityElegant for tree problemsClear control flow
RiskStack overflowInfinite loop

Example 1: Fibonacci

Recursive

int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}

Time: O(2ⁿ) → Exponential!

Iterative

int fib(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1, c;
    for (int i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

Time: O(n)


Example 2: Binary Search

Recursive

int binarySearch(int arr[], int l, int r, int x) {
    if (l > r) return -1;
    int mid = l + (r - l) / 2;
    if (arr[mid] == x) return mid;
    if (arr[mid] > x)
        return binarySearch(arr, l, mid - 1, x);
    return binarySearch(arr, mid + 1, r, x);
}

Iterative

int binarySearch(int arr[], int n, int x) {
    int l = 0, r = n - 1;
    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (arr[mid] == x) return mid;
        if (arr[mid] > x) r = mid - 1;
        else l = mid + 1;
    }
    return -1;
}

Example 3: Tower of Hanoi

Recursive

void hanoi(int n, char from, char to, char aux) {
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", from, to);
        return;
    }
    hanoi(n-1, from, aux, to);
    printf("Move disk %d from %c to %c\n", n, from, to);
    hanoi(n-1, aux, to, from);
}

Time: O(2ⁿ − 1)


Tail Recursion → Iteration

Tail Recursive Fibonacci:

int fib_tail(int n, int a, int b) {
    if (n == 0) return a;
    if (n == 1) return b;
    return fib_tail(n-1, b, a + b);
}
// Call: fib_tail(n, 0, 1)

Converted to Iteration:

int fib(int n) {
    return fib_tail(n, 0, 1);
}

6. QUEUE – Abstract Data Type

FIFO (First In, First Out)

Operations

OperationDescription
enqueue(x)Add at rear
dequeue()Remove from front
front()View front
rear()View rear
isEmpty()Check empty

7. Array Implementation (Simple Queue)

#define MAX 100
typedef struct {
    int arr[MAX];
    int front, rear;
} Queue;

void init(Queue* q) {
    q->front = q->rear = -1;
}

void enqueue(Queue* q, int x) {
    if (q->rear == MAX - 1) {
        printf("Queue Full!\n");
        return;
    }
    if (q->front == -1) q->front = 0;
    q->arr[++q->rear] = x;
}

int dequeue(Queue* q) {
    if (q->front == -1) return -1;
    int x = q->arr[q->front];
    if (q->front == q->rear)
        q->front = q->rear = -1;
    else
        q->front++;
    return x;
}

Problem: Space wastage → Use Circular Queue


8. Circular Queue

void enqueue(Queue* q, int x) {
    if ((q->rear + 1) % MAX == q->front) {
        printf("Full!\n"); return;
    }
    if (q->front == -1) q->front = 0;
    q->rear = (q->rear + 1) % MAX;
    q->arr[q->rear] = x;
}

int dequeue(Queue* q) {
    if (q->front == -1) return -1;
    int x = q->arr[q->front];
    if (q->front == q->rear)
        q->front = q->rear = -1;
    else
        q->front = (q->front + 1) % MAX;
    return x;
}

Circular Queue Diagram

Index:  0   1   2   3   4
      [D]  [] [A] [B] [C]
           ↑        ↑
        front     rear

9. Linked List Queue

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

typedef struct {
    Node *front, *rear;
} Queue;

void enqueue(Queue* q, int x) {
    Node* n = (Node*)malloc(sizeof(Node));
    n->data = x; n->next = NULL;
    if (q->rear == NULL) {
        q->front = q->rear = n;
    } else {
        q->rear-> minerales = n;
        q->rear = n;
    }
}

10. Deque (Double-Ended Queue)

  • Insert/delete from both ends
  • Can simulate stack and queue
pushFront(), pushBack(), popFront(), popBack()

11. Priority Queue

Elements removed by priority, not insertion order.

Implementation

  • Binary Heap → O(log n) insert/extract
  • Array → O(n) extract
  • Linked List → O(n) insert

Min-Heap Priority Queue (Array)

void insert(PQ* pq, int x) {
    pq->heap[++pq->size] = x;
    int i = pq->size;
    while (i > 1 && pq->heap[i] < pq->heap[i/2]) {
        swap(&pq->heap[i], &pq->heap[i/2]);
        i /= 2;
    }
}

12. Tradeoffs: Iteration vs Recursion

FactorRecursionIteration
MemoryO(n) stackO(1)
SpeedSlowerFaster
CodeCleanVerbose
Best ForTrees, Divide & ConquerLoops, large n

Prefer iteration when:

  • Performance critical
  • Large input
  • Stack overflow risk

13. Summary Table

FeatureStackQueue
OrderLIFOFIFO
InsertTopRear
DeleteTopFront
UseRecursion, ExpressionsBFS, Scheduling
CircularNoYes
PriorityCan beCommon

14. Problem Solving Examples

1. Reverse String using Stack

void reverse(char* str) {
    Stack s; init(&s);
    for(int i = 0; str[i]; i++) push(&s, str[i]);
    for(int i = 0; str[i]; i++) str[i] = pop(&s);
}

2. Next Greater Element

void nextGreater(int arr[], int n) {
    Stack s; init(&s);
    for(int i = n-1; i >= 0; i--) {
        while(!isEmpty(&s) && peek(&s) <= arr[i]) pop(&s);
        int nge = isEmpty(&s) ? -1 : peek(&s);
        printf("%d ", nge);
        push(&s, arr[i]);
    }
}

15. Practice Problems

  1. Infix to Postfix Conversion
  2. Check balanced parentheses
  3. Implement stack using two queues
  4. Implement queue using two stacks
  5. Stock Span Problem
  6. LRU Cache using stack + hash

Key Takeaways

ConceptInsight
StackUse for depth, undo, expressions
QueueUse for breadth, order, scheduling
RecursionElegant, but risky
IterationSafe, fast, preferred in production
Circular QueueSolves space wastage
Priority QueueEssential for Dijkstra, Huffman

End of Notes