Complete Notes on Stacks and Queues
With Code Examples, Structures, Diagrams & Applications
Complete Notes on Stacks and Queues
With Code Examples, Structures, Diagrams & Applications
Complete Notes on Stacks and Queues
With Code Examples, Structures, Diagrams & Applications
1. STACK
Definition
LIFO (Last In, First Out)
The last element added is the first one to be removed.
Analogy
A stack of plates: You push a plate on top, and pop from the top.
[Plate 3] ← Top
[Plate 2]
[Plate 1]
Basic Operations
| Operation | Description | Time Complexity |
|---|---|---|
push(x) |
Insert element, x at top | O(1) |
pop() |
Remove and return top | O(1) |
peek() / top() |
View top element | O(1) |
isEmpty() |
Check if stack is empty | O(1) |
size() |
Return number of elements | O(1) |
Implementation 1: Array-Based Stack
#include <stdio.h>
#define MAX 100
typedef struct {
int arr[MAX];
int top;
} Stack;
void initStack(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 data) {
if(isFull(s)) {
printf("Stack Overflow!\n");
return;
}
s->arr[++(s->top)] = data;
}
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];
}
// Example Usage
int main() {
Stack s;
initStack(&s);
push(&s, 10);
push(&s, 20);
push(&s, 30);
printf("Top: %d\n", peek(&s)); // 30
printf("Pop: %d\n", pop(&s)); // 30
printf("Pop: %d\n", pop(&s)); // 20
return 0;
}
Implementation 2: Linked List-Based Stack
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node* top;
} Stack;
void initStack(Stack* s) {
s->top = NULL;
}
void push(Stack* s, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = s->top;
s->top = newNode;
}
int pop(Stack* s) {
if(s->top == NULL) {
printf("Stack Empty!\n");
return -1;
}
Node* temp = s->top;
int data = temp->data;
s->top = s->top->next;
free(temp);
return data;
}
int peek(Stack* s) {
return (s->top != NULL) ? s->top->data : -1;
}
Structure Diagram (Array Stack)
Index: 0 1 2 3
+----+----+----+----+
Array: | 10 | 20 | 30 | |
+----+----+----+----+
↑
top = 2
Applications of Stack
| Application | Use |
|---|---|
| Function Call | Call stack in recursion |
| Expression Evaluation | Infix → Postfix → Evaluate |
| Parentheses Matching | Check balanced (), {}, [] |
| Undo Operation | In editors |
| Backtracking | Maze, N-Queen |
Example: Parentheses Matching
int isBalanced(char* exp) {
Stack s;
initStack(&s);
for(int i = 0; exp[i] != '\0'; i++) {
if(exp[i] == '(' || exp[i] == '{' || exp[i] == '[')
push(&s, exp[i]);
else if(exp[i] == ')' || exp[i] == '}' || exp[i] == ']') {
if(isEmpty(&s)) return 0;
char top = peek(&s);
if((exp[i] == ')' && top != '(') ||
(exp[i] == '}' && top != '{') ||
(exp[i] == ']' && top != '['))
return 0;
pop(&s);
}
}
return isEmpty(&s);
}
2. QUEUE
Definition
FIFO (First In, First Out)
First element added is the first one to be removed.
Analogy
A line at a ticket counter: First come, first served.
Front → [A] → [B] → [C] → [D] ← Rear
Basic Operations
| Operation | Description | Time Complexity |
|---|---|---|
enqueue(x) |
Add x at rear | O(1) |
dequeue() |
Remove from front | O(1) |
front() |
View front element | O(1) |
rear() |
View rear element | O(1) |
isEmpty() |
Check if empty | O(1) |
Implementation 1: Simple Array Queue (Fixed Size)
#define MAX 100
typedef struct {
int arr[MAX];
int front, rear;
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = -1;
}
int isEmpty(Queue* q) {
return q->front == -1;
}
int isFull(Queue* q) {
return q->rear == MAX - 1;
}
void enqueue(Queue* q, int data) {
if(isFull(q)) {
printf("Queue Full!\n");
return;
}
if(isEmpty(q))
q->front = 0;
q->arr[++(q->rear)] = data;
}
int dequeue(Queue* q) {
if(isEmpty(q)) {
printf("Queue Empty!\n");
return -1;
}
int data = q->arr[q->front];
if(q->front == q->rear)
q->front = q->rear = -1;
else
q->front++;
return data;
}
Problem: Space wastage after dequeue → Use Circular Queue
Implementation 2: Circular Queue
#define MAX 5
typedef struct {
int arr[MAX];
int front, rear;
} CQueue;
void init(CQueue* q) {
q->front = q->rear = -1;
}
int isFull(CQueue* q) {
return (q->rear + 1) % MAX == q->front;
}
int isEmpty(CQueue* q) {
return q->front == -1;
}
void enqueue(CQueue* q, int data) {
if(isFull(q)) {
printf("Queue Full!\n");
return;
}
if(isEmpty(q))
q->front = 0;
q->rear = (q->rear + 1) % MAX;
q->arr[q->rear] = data;
}
int dequeue(CQueue* q) {
if(isEmpty(q)) return -1;
int data = q->arr[q->front];
if(q->front == q->rear)
q->front = q->rear = -1;
else
q->front = (q->front + 1) % MAX;
return data;
}
Circular Queue Diagram
Index: 0 1 2 3 4
+---+---+---+---+---+
| D | | A | B | C |
+---+---+---+---+---+
↑ ↑
front rear
Implementation 3: Queue using Linked List
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node *front, *rear;
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = NULL;
}
void enqueue(Queue* q, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
if(q->rear == NULL) {
q->front = q->rear = newNode;
return;
}
q->rear->next = newNode;
q->rear = newNode;
}
int dequeue(Queue* q) {
if(q->front == NULL) return -1;
Node* temp = q->front;
int data = temp->data;
q->front = q->front->next;
if(q->front == NULL)
q->rear = NULL;
free(temp);
return data;
}
Applications of Queue
| Application | Use |
|---|---|
| CPU Scheduling | Round Robin |
| Breadth-First Search (BFS) | Graph traversal |
| Printer Spooling | Print jobs |
| Message Queues | Inter-process communication |
| Simulation | Bank queue, traffic lights |
Comparison: Stack vs Queue
| Feature | Stack | Queue |
|---|---|---|
| Principle | LIFO | FIFO |
| Insertion | Top (push) | Rear (enqueue) |
| Deletion | Top (pop) | Front (dequeue) |
| Access | Only top | Front & Rear |
| Use Case | Recursion, Undo | BFS, Scheduling |
Special Types
1. Deque (Double-Ended Queue)
- Insert/delete from both ends
- Can act as Stack or Queue
// Operations: pushFront, pushBack, popFront, popBack
2. Priority Queue
- Elements have priority
- Highest priority removed first
- Implemented using Heap
Summary Table
| Data Structure | Order | Insertion | Deletion | Use Case |
|---|---|---|---|---|
| Stack | LIFO | O(1) | O(1) | Function calls |
| Simple Queue | FIFO | O(1) | O(1) | Limited space |
| Circular Queue | FIFO | O(1) | O(1) | Efficient space |
| Linked Queue | FIFO | O(1) | O(1) | Dynamic size |
Practice Problems
- Reverse a string using stack
- Implement queue using two stacks
- Check palindrome using stack
- Implement stack using two queues
- Evaluate postfix expression
Key Takeaway:
Stack → For depth and recursion
Queue → For breadth and order preservation
End of Notes
Complete Notes on Stacks and Queues
With Code Examples, Structures, Diagrams & Applications
Complete Notes on Stacks and Queues
With Code Examples, Structures, Diagrams & Applications
Complete Notes on Stacks and Queues
With Code Examples, Structures, Diagrams & Applications
1. STACK
Definition
LIFO (Last In, First Out)
The last element added is the first one to be removed.
Analogy
A stack of plates: You push a plate on top, and pop from the top.
[Plate 3] ← Top
[Plate 2]
[Plate 1]
Basic Operations
| Operation | Description | Time Complexity |
|---|---|---|
push(x) |
Insert element, x at top | O(1) |
pop() |
Remove and return top | O(1) |
peek() / top() |
View top element | O(1) |
isEmpty() |
Check if stack is empty | O(1) |
size() |
Return number of elements | O(1) |
Implementation 1: Array-Based Stack
#include <stdio.h>
#define MAX 100
typedef struct {
int arr[MAX];
int top;
} Stack;
void initStack(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 data) {
if(isFull(s)) {
printf("Stack Overflow!\n");
return;
}
s->arr[++(s->top)] = data;
}
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];
}
// Example Usage
int main() {
Stack s;
initStack(&s);
push(&s, 10);
push(&s, 20);
push(&s, 30);
printf("Top: %d\n", peek(&s)); // 30
printf("Pop: %d\n", pop(&s)); // 30
printf("Pop: %d\n", pop(&s)); // 20
return 0;
}
Implementation 2: Linked List-Based Stack
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node* top;
} Stack;
void initStack(Stack* s) {
s->top = NULL;
}
void push(Stack* s, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = s->top;
s->top = newNode;
}
int pop(Stack* s) {
if(s->top == NULL) {
printf("Stack Empty!\n");
return -1;
}
Node* temp = s->top;
int data = temp->data;
s->top = s->top->next;
free(temp);
return data;
}
int peek(Stack* s) {
return (s->top != NULL) ? s->top->data : -1;
}
Structure Diagram (Array Stack)
Index: 0 1 2 3
+----+----+----+----+
Array: | 10 | 20 | 30 | |
+----+----+----+----+
↑
top = 2
Applications of Stack
| Application | Use |
|---|---|
| Function Call | Call stack in recursion |
| Expression Evaluation | Infix → Postfix → Evaluate |
| Parentheses Matching | Check balanced (), {}, [] |
| Undo Operation | In editors |
| Backtracking | Maze, N-Queen |
Example: Parentheses Matching
int isBalanced(char* exp) {
Stack s;
initStack(&s);
for(int i = 0; exp[i] != '\0'; i++) {
if(exp[i] == '(' || exp[i] == '{' || exp[i] == '[')
push(&s, exp[i]);
else if(exp[i] == ')' || exp[i] == '}' || exp[i] == ']') {
if(isEmpty(&s)) return 0;
char top = peek(&s);
if((exp[i] == ')' && top != '(') ||
(exp[i] == '}' && top != '{') ||
(exp[i] == ']' && top != '['))
return 0;
pop(&s);
}
}
return isEmpty(&s);
}
2. QUEUE
Definition
FIFO (First In, First Out)
First element added is the first one to be removed.
Analogy
A line at a ticket counter: First come, first served.
Front → [A] → [B] → [C] → [D] ← Rear
Basic Operations
| Operation | Description | Time Complexity |
|---|---|---|
enqueue(x) |
Add x at rear | O(1) |
dequeue() |
Remove from front | O(1) |
front() |
View front element | O(1) |
rear() |
View rear element | O(1) |
isEmpty() |
Check if empty | O(1) |
Implementation 1: Simple Array Queue (Fixed Size)
#define MAX 100
typedef struct {
int arr[MAX];
int front, rear;
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = -1;
}
int isEmpty(Queue* q) {
return q->front == -1;
}
int isFull(Queue* q) {
return q->rear == MAX - 1;
}
void enqueue(Queue* q, int data) {
if(isFull(q)) {
printf("Queue Full!\n");
return;
}
if(isEmpty(q))
q->front = 0;
q->arr[++(q->rear)] = data;
}
int dequeue(Queue* q) {
if(isEmpty(q)) {
printf("Queue Empty!\n");
return -1;
}
int data = q->arr[q->front];
if(q->front == q->rear)
q->front = q->rear = -1;
else
q->front++;
return data;
}
Problem: Space wastage after dequeue → Use Circular Queue
Implementation 2: Circular Queue
#define MAX 5
typedef struct {
int arr[MAX];
int front, rear;
} CQueue;
void init(CQueue* q) {
q->front = q->rear = -1;
}
int isFull(CQueue* q) {
return (q->rear + 1) % MAX == q->front;
}
int isEmpty(CQueue* q) {
return q->front == -1;
}
void enqueue(CQueue* q, int data) {
if(isFull(q)) {
printf("Queue Full!\n");
return;
}
if(isEmpty(q))
q->front = 0;
q->rear = (q->rear + 1) % MAX;
q->arr[q->rear] = data;
}
int dequeue(CQueue* q) {
if(isEmpty(q)) return -1;
int data = q->arr[q->front];
if(q->front == q->rear)
q->front = q->rear = -1;
else
q->front = (q->front + 1) % MAX;
return data;
}
Circular Queue Diagram
Index: 0 1 2 3 4
+---+---+---+---+---+
| D | | A | B | C |
+---+---+---+---+---+
↑ ↑
front rear
Implementation 3: Queue using Linked List
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node *front, *rear;
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = NULL;
}
void enqueue(Queue* q, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
if(q->rear == NULL) {
q->front = q->rear = newNode;
return;
}
q->rear->next = newNode;
q->rear = newNode;
}
int dequeue(Queue* q) {
if(q->front == NULL) return -1;
Node* temp = q->front;
int data = temp->data;
q->front = q->front->next;
if(q->front == NULL)
q->rear = NULL;
free(temp);
return data;
}
Applications of Queue
| Application | Use |
|---|---|
| CPU Scheduling | Round Robin |
| Breadth-First Search (BFS) | Graph traversal |
| Printer Spooling | Print jobs |
| Message Queues | Inter-process communication |
| Simulation | Bank queue, traffic lights |
Comparison: Stack vs Queue
| Feature | Stack | Queue |
|---|---|---|
| Principle | LIFO | FIFO |
| Insertion | Top (push) | Rear (enqueue) |
| Deletion | Top (pop) | Front (dequeue) |
| Access | Only top | Front & Rear |
| Use Case | Recursion, Undo | BFS, Scheduling |
Special Types
1. Deque (Double-Ended Queue)
- Insert/delete from both ends
- Can act as Stack or Queue
// Operations: pushFront, pushBack, popFront, popBack
2. Priority Queue
- Elements have priority
- Highest priority removed first
- Implemented using Heap
Summary Table
| Data Structure | Order | Insertion | Deletion | Use Case |
|---|---|---|---|---|
| Stack | LIFO | O(1) | O(1) | Function calls |
| Simple Queue | FIFO | O(1) | O(1) | Limited space |
| Circular Queue | FIFO | O(1) | O(1) | Efficient space |
| Linked Queue | FIFO | O(1) | O(1) | Dynamic size |
Practice Problems
- Reverse a string using stack
- Implement queue using two stacks
- Check palindrome using stack
- Implement stack using two queues
- Evaluate postfix expression
Key Takeaway:
Stack → For depth and recursion
Queue → For breadth and order preservation
End of Notes