Complete Notes on Stacks and Queues

With C Code, Diagrams, Examples, and Problem Solving

Complete Notes on Stacks and Queues

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

Operation Description Time
push(x) Insert x at top O(1)
pop() Remove and return top O(1)
peek() / top() View top element O(1)
isEmpty() Check if empty O(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

Type Example
Infix A + 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 -

Token Stack
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

Feature Recursion Iteration
Mechanism Function calls itself Loops
Memory Stack (call stack) Constant
Speed Slower (function call overhead) Faster
Readability Elegant for tree problems Clear control flow
Risk Stack overflow Infinite 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)


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

Operation Description
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

Factor Recursion Iteration
Memory O(n) stack O(1)
Speed Slower Faster
Code Clean Verbose
Best For Trees, Divide & Conquer Loops, large n

Prefer iteration when:
- Performance critical
- Large input
- Stack overflow risk


13. Summary Table

Feature Stack Queue
Order LIFO FIFO
Insert Top Rear
Delete Top Front
Use Recursion, Expressions BFS, Scheduling
Circular No Yes
Priority Can be Common

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

Concept Insight
Stack Use for depth, undo, expressions
Queue Use for breadth, order, scheduling
Recursion Elegant, but risky
Iteration Safe, fast, preferred in production
Circular Queue Solves space wastage
Priority Queue Essential for Dijkstra, Huffman

End of Notes

Last updated: Nov 12, 2025

Complete Notes on Stacks and Queues

With C Code, Diagrams, Examples, and Problem Solving

Complete Notes on Stacks and Queues

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

Operation Description Time
push(x) Insert x at top O(1)
pop() Remove and return top O(1)
peek() / top() View top element O(1)
isEmpty() Check if empty O(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

Type Example
Infix A + 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 -

Token Stack
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

Feature Recursion Iteration
Mechanism Function calls itself Loops
Memory Stack (call stack) Constant
Speed Slower (function call overhead) Faster
Readability Elegant for tree problems Clear control flow
Risk Stack overflow Infinite 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)


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

Operation Description
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

Factor Recursion Iteration
Memory O(n) stack O(1)
Speed Slower Faster
Code Clean Verbose
Best For Trees, Divide & Conquer Loops, large n

Prefer iteration when:
- Performance critical
- Large input
- Stack overflow risk


13. Summary Table

Feature Stack Queue
Order LIFO FIFO
Insert Top Rear
Delete Top Front
Use Recursion, Expressions BFS, Scheduling
Circular No Yes
Priority Can be Common

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

Concept Insight
Stack Use for depth, undo, expressions
Queue Use for breadth, order, scheduling
Recursion Elegant, but risky
Iteration Safe, fast, preferred in production
Circular Queue Solves space wastage
Priority Queue Essential for Dijkstra, Huffman

End of Notes

Last updated: Nov 12, 2025