You are on page 1of 42

Data Structures: Stack and Queue

Department of CSE

1
Stack Introduction

1. Stack is basically a data object .


2. A stack is a data structure in which
items can be inserted only from one
end and get items back from the same
end.
3. There , the last item inserted into stack,
is the first item to be taken out from
the stack.
4. In short its also called Last in First out
[LIFO]. (The last element inserted will
be the first to be retrieved)

2
Practical daily life :
a pile of heavy books
kept in a vertical
box,dishes kept one
on top of another

In computer world : In
processing of
subroutine calls and
returns ; there is an
explicit use of stack of
return addresses.
Also in evaluation of
arithmetic expressions
, stack is used.
Operations on Stack
Primary operations defined on a stack:
.PUSH : add an element at the top of the list.
.POP : remove the at the top of the list.
.Also "IsEmpty()" and IsFull" function, which tests whether a stack is empty or full respectively.

Implementations of stacks using


1. array
2. linked list
Push and Pop
• Primary operations: Push and Pop
• Push
• Add an element to the top of the stack
• Pop
• Remove the element at the top of the stack
• "IsEmpty()" and IsFull" functions

empty stack push an element push another pop push another Stack full
top
C
top top A
B C
C
top top
A A A A
top A

5
Implementation of stack using array
• Need to declare an array size ahead of time
• Associated with each stack is TopOfStack
• for an empty stack, set TopOfStack to -1
• Push
• (1) Increment TopOfStack by 1.
• (2) Set Stack[TopOfStack] = X
• Pop
• (1) Set return value to Stack[TopOfStack]
• (2) Decrement TopOfStack by 1
• Any time TopofStack is equal to size of array then stack is full.
• These operations are performed in constant time

6
Implementation of stack using array
Push ( ):
Description: Here STACK is an array with MAX locations. TOP points to the
top most element and ITEM is the value to be inserted.

7
Implementation of stack using array
Pop ( ):
Description: Here STACK is an array with MAX locations. TOP points to the top most element.
#include<stdio.h> void push(int value){
#define SIZE 10 if(top == SIZE-1)
void push(int); printf("\nStack is Full!!! Insertion is not possible!!!");
void pop(); else{
void display(); top++;
int stack[SIZE], top = -1; stack[top] = value;
void main() printf("\nInsertion success!!!");
{ }
int value, choice; }
while(1){ void pop(){
printf("\n\n***** MENU *****\n"); if(top == -1)
printf("1. Push\n2. Pop\n3. Display\n4. Exit"); printf("\nStack is Empty!!! Deletion is not possible!!!");
printf("\nEnter your choice: "); else{
scanf("%d",&choice); printf("\nDeleted : %d", stack[top]);
switch(choice){ top--;
case 1: printf("Enter the value to be insert: "); }
scanf("%d",&value); }
push(value); void display(){
break; if(top == -1)
case 2: pop(); printf("\nStack is Empty!!!");
break; else{
case 3: display(); int i;
break; printf("\nStack elements are:\n");
case 4: exit(0); for(i=top; i>=0; i--)
default: printf("\nWrong selection!!! Try again!!!"); printf("%d\n",stack[i]);
} }
} } 9
}
#include<stdio.h>
#define SIZE 10
void push(int);
void pop(); Peek() is one of a stack operation that prints the value of the
void display(); top most element of the stack without deleting that element
int stack[SIZE], top = -1; from the stack.
void main()
{
int value, choice;
while(1){
printf("\n\n***** MENU *****\n");
printf("1. Push\n2. Pop\n3. Peek \n 4. Display 5. Exit");
printf("\nEnter your choice: ");
scanf("%d",&choice); void peek(){
switch(choice){
case 1: printf("Enter the value to be insert: ");
if(top == -1)
scanf("%d",&value); printf("\nStack is Empty!!!");
push(value); else{
break; int i;
case 2: pop(); printf("\nStack top most element is: %d\n“,stack[top]);
break;
case 3: peek();
}
break; }
case 4: display();
break;
case 5: exit(0);
default: printf("\nWrong selection!!! Try again!!!");
}
} 10
}
Implementation of stack using Linked List
Push ( ):
void push(int data) {
#include <stdio.h> if (top == NULL)
#include <stdlib.h> {
top =(struct node *)malloc(sizeof(struct node));
// Structure to create a node with data top->ptr = NULL;
and the next pointer top->info = data;
struct node { }
int info; else
struct node *ptr; {
}*top,*top1,*temp; temp =(struct node *)malloc(sizeof(struct node));
temp->ptr = top;
int count = 0; temp->info = data;
top = temp;
}
count++;
printf("Node is Inserted\n\n");
}

11
Implementation of stack using Linked List
Pop ( ):
int pop() {
#include <stdio.h> top1 = top;
#include <stdlib.h>
if (top1 == NULL)
// Structure to create a node with data {
and the next pointer printf("\nStack Underflow\n");
struct node { return -1;
int info; }
struct node *ptr; else
}*top,*top1,*temp; top1 = top1->ptr;
int popped = top->info;
int count = 0; free(top);
top = top1;
count--;
return popped;
}

12
Application of stack
Advantages
• Function calls and recursion • Easy implementation

• Undo/Redo operations • Efficient memory utilization


• Fast access time
• Expression evaluation • Helps in function calls

• Browser history • Enables undo/redo operations

• Balanced Parentheses

• Backtracking Algorithms Disadvantages


• No random access
• Not suitable for certain applications
• Stack overflow and underflow

13
Expression evaluation

A*B+C*D

(A + (B * C))

+A+B+CD

2. To check that every right brace, bracket, and parentheses must correspond to its left counterpart
e.g. [( )] is legal, but [( ] ) is illegal
Expression evaluation

Rules
1. Priority of operators
i. Exponent (^)
ii. Division, Multiplication (/,*)
iii. Addition, Subtraction (+,-)
2. No two operators of same priority can stay together in the stack column
3. Lowest priority operator can not be placed before higher priority operator
4. Operators between matching brackets are Popped

15
Expression evaluation
Infix to Postfix using stack ...

16
Expression evaluation
Evaluating Arithmetic Postfix Expression

17
Expression evaluation
Infix to Prefix using stack ...

• Step 1: Reverse the infix string. Note that while reversing the string you must

interchange left and right parentheses.

• Step 2: Obtain the postfix expression of the infix expression Step 1.

• Step 3: Reverse the postfix expression to get the prefix expression

18
Application of stack
Balanced Parentheses
1. Get the expression “Exp[]”
2. While Exp[i] != NULL, repeat
3. If Exp[i] = ( or { or [
4. Push Exp[i] to stack
5. End If
6. If Exp[i] = ) or } or ]
7. Pop stack top
8. Match stack top with similar type opening bracket
9. Return if matching unsuccessful (“Unbalanced”)
10. Continue otherwise
11. End While
12. If stack is empty, return “Balanced”

19
Application of stack
Recursive Function Call
Types of recursion:
1.Direct Recursion

2.Indirect Recursion

3.Tail Recursion

4.No Tail/ Head Recursion

5.Linear recursion

6.Tree Recursion

20
Application of stack
Direct vs Indirect Recursion Indirect Recursion: When a function is mutually called by
another function in a circular manner.

Direct Recursion: When a function calls fun1()


itself within the same function repeatedly. {
// write some code
fun2()
}
fun()
{ fun2()
// write some code {
fun(); // write some code
// some code fun3()
} // write some code
}

fun3()
{
// write some code
fun1()
} 21
Application of stack
Tail Recursion Non-Tail/Head Recursion
Tail-recursion: if the function makes Non-tail/head recursion: if a function
recursive calling itself, and that makes a recursive call itself, the
recursive call is the last statement recursive call will be the first statement
executes by the function. in the function.
All operations are done at the return
time.

void fun1( int num) void head_fun (int num)


{ {
if (num == 0) if ( num > 0 )
return; {
else head_fun (num -1);
printf ("\n Number is: %d", num); printf (" %d", num);
return fun1 (num - 1); }
} }

22
Application of stack
Linear Recursion Tree Recursion
Linear recursion: if the function makes In Tree recursion, Instead of a single
a single call to itself at each time the function call there are more than one
function runs and grows linearly in recursive calls for each non-base case.
proportion to the size of the problem. Functions with two recursive calls are
referred to as binary recursive functions.

int factorial(int n) int fibo_num (int num)


{ {
if (n==1) if (num <= 1)
return 1; //base case return num;
else return fibo_num (num - 1 ) + fibo_num(num
return n*factorial(n-1); //recursive case - 2);
} }

23
Queue Introduction
• Like a stack, a queue is also a list. However, with a queue, insertion is done at one end, while
deletion is performed at the other end.
• Accessing the elements of queues follows a First In, First Out (FIFO) order.
• Like customers standing in a check-out line in a store, the first customer in is the first
customer served.

Example :
1. PRACTICAL EXAMPLE : A line at a ticket counter for buying tickets operates on above rules.

2. IN COMPUTER WORLD : In a batch processing system, jobs are queued up for processing.
24
Operations on Queue
Primary operations defined on a Queue:
.EnQueue : This is used to add elements into the queue at the back end.
.DeQueue : This is used to delete elements from a queue from the front end.
.Also "IsEmpty()" and "IsFull()" can be defined to test whether the queue is Empty or full.

Implementations of stacks using


1. array
2. linked list

Remove Insert
(Dequeue) front rear (Enqueue)

25
Queue Implementation using array
• There are several different algorithms to implement Enqueue and Dequeue
• When enqueuing, the front index is always fixed and the rear index moves forward
in the array.
rear rear rear rear rear

3 3 6 3 6 9 6 9 9

front front front front front


Enqueue(3) Enqueue(6) Enqueue(9) Dequeue() Dequeue()

rear = -1
• When dequeuing, the rear index is always fixed and the front
index moves forward in the array.
front=-1
Dequeue()
Queue Implementation using array
Insert ( ): or EnQueue():
Description: Here QUEUE is an array with N locations. FRONT and REAR points to the front and rear of the QUEUE. ITEM is
the value to be inserted.

void enQueue(int value){


if(rear == SIZE-1)
printf("\nQueue is Full!!! Insertion is not possible!!!");
else{
if(front == -1)
front = 0;
rear++;
queue[rear] = value;
printf("\nInsertion success!!!");
}
}
Queue Implementation using array
Delete ( ): or DeQueue():
Description: Here QUEUE is an array with N locations. FRONT and REAR points to the front and rear of the QUEUE.

void deQueue(){
if(front == rear)
printf("\nQueue is Empty!!! Deletion is not possible!!!");
else{
printf("\nDeleted : %d", queue[front]);
front++;
if(front == rear)
front = rear = -1;
}
}
#include<stdio.h> void enQueue(int value){
#define SIZE 10 if(rear == SIZE-1)
void enQueue(int); Simple Queue printf("\nQueue is Full!!! Insertion is not possible!!!");
void deQueue(); else{
void display(); if(front == -1)
int queue[10], front = -1, rear = -1; front = 0;
rear++;
void main() {
queue[rear] = value;
int value, choice;
printf("\nInsertion success!!!");
while(1){ }}
printf("\n\n***** MENU *****\n"); void deQueue(){
printf("1. Insertion\n2. Deletion\n3. Display\n4. Exit"); if(front == rear)
printf("\nEnter your choice: "); printf("\nQueue is Empty!!! Deletion is not possible!!!");
scanf("%d",&choice); else{
switch(choice){ printf("\nDeleted : %d", queue[front]);
case 1: printf("Enter the value to be insert: "); front++;
scanf("%d",&value); if(front == rear)
enQueue(value); front = rear = -1;
break; }}
case 2: deQueue(); void display(){
break; if(rear == -1)
case 3: display(); printf("\nQueue is Empty!!!");
break; else{
int i;
case 4: exit(0);
printf("\nQueue elements are:\n");
default: printf("\nWrong selection!!! Try again!!!");
for(i=front; i<=rear; i++)
} printf("%d\t",queue[i]); 29
}} }}
// Linked list based implementation of queue
#include <stdio.h> Simple Queue Using Linked List
#include <stdlib.h>
// Driver code
// A linked list (LL) node to store a queue entry int main()
struct QNode { {
int key; struct Queue* q = createQueue();
struct QNode* next; enQueue(q, 10);
}; enQueue(q, 20);
deQueue(q);
// The queue, front stores the front node of LL and rear deQueue(q);
// stores the last node of LL enQueue(q, 30);
struct Queue { enQueue(q, 40);
struct QNode *front, *rear; enQueue(q, 50);
}; deQueue(q);
printf("Queue Front : %d \n", ((q->front != NULL) ? (q->front)->key
// A utility function to create an empty queue : -1));
struct Queue* createQueue() printf("Queue Rear : %d", ((q->rear != NULL) ? (q->rear)->key :
{ -1));
struct Queue* q return 0;
= (struct Queue*)malloc(sizeof(struct Queue)); }
q->front = q->rear = NULL;
return q;
}

30
// A utility function to create a new linked list node.
struct QNode* newNode(int k) Simple Queue Using Linked List
{
struct QNode* temp
= (struct QNode*)malloc(sizeof(struct QNode));
temp->key = k;
temp->next = NULL;
return temp;
} // Function to remove a key from given queue q
void deQueue(struct Queue* q)
// The function to add a key k to q {
void enQueue(struct Queue* q, int k) // If queue is empty, return NULL.
{ if (q->front == NULL)
// Create a new LL node return;
struct QNode* temp = newNode(k);
// Store previous front and move front one node ahead
// If queue is empty, then new node is front and rear struct QNode* temp = q->front;
// both
if (q->rear == NULL) { q->front = q->front->next;
q->front = q->rear = temp;
return; // If front becomes NULL, then change rear also as NULL
} if (q->front == NULL)
// Add the new node at the end of queue and change rear q->rear = NULL;
q->rear->next = temp;
q->rear = temp; free(temp);
} }
31
Circular Queue
Problem of Simple Queue: rear
• After the “rear” reached queue capacity, Dequeue() of elements from the front
can leave unutilized spaces.
9
Circular Queue:
• The element of the queue act as a circular ring.
front
• The last element of the queue is connected to the first element of the queue
forming a circle.

Applications of Circular Queue:


• Memory Management: The unused memory locations in the case of ordinary
queues can be utilized in circular queues.
• Traffic system: In computer controlled traffic system, circular queues are used
to switch on the traffic lights one by one repeatedly as per the time set.
• CPU Scheduling: Operating systems often maintain a queue of processes that
are ready to execute or that are waiting for a particular event to occur.

32
Queue Implementation using circular array

Ring buffer
Queue Implementation using circular array

34
#include<stdio.h> void enQueue(int value){
#define SIZE 10 if((front==0 &&rear == SIZE-1) || front==rear+1)
void enQueue(int); Circular Queue printf("\nQueue is Full!!! Insertion is not possible!!!");
void deQueue(); else{
void display(); if(front == -1)
int queue[10], front = -1, rear = -1; front = 0;
rear=(rear+1)%SIZE;
void main() {
queue[rear] = value;
int value, choice;
printf("\nInsertion success!!!");
while(1){ }}
printf("\n\n***** MENU *****\n"); void deQueue(){
printf("1. Insertion\n2. Deletion\n3. Display\n4. Exit"); if(front == -1)
printf("\nEnter your choice: "); printf("\nQueue is Empty!!! Deletion is not possible!!!");
scanf("%d",&choice); else{
switch(choice){ printf("\nDeleted : %d", queue[front]);
case 1: printf("Enter the value to be insert: "); front=(front+1)%SIZE;
scanf("%d",&value); if(front == rear)
enQueue(value); front = rear = -1;
break; }}
case 2: deQueue(); void display(){
break; if(front == -1)
case 3: display(); printf("\nQueue is Empty!!!");
break; else{
int i;
case 4: exit(0);
printf("\nQueue elements are:\n");
default: printf("\nWrong selection!!! Try again!!!");
for(i=front; i!=rear; i=(i+1)%SIZE)
} printf("%d\t",queue[i]); 35
}} }}
Priority Queue
• It arranges the elements in a queue based on some priority.
• Each element has a priority value associated with it.
• When an element is added to the queue, it is inserted in a position based on its priority value.
• Can be implemented using – array, linked list, heap, binary search tree.
• Types of priority queue:
• Ascending Order Priority Queue
• Descending order Priority Queue
• Rule of priority instead of the FIFO

Operations on Priority Queue:


• enqueue(): This function is used to insert new data into the queue.
• dequeue(): This function removes the element with the highest priority from the queue.
• peek()/top(): This function is used to get the highest priority element in the queue without removing
it from the queue.

36
Priority Queue
• Applications:
• Dijkstra's algorithm, A* Search algorithm
• Implementing stack
• Finding Kth largest/smallest element
• Load balancing and interrupt handling in an operating system
• Data compression in Huffman code
• Advantages:
• It helps to access the elements in a faster way
• Dynamic reordering of the elements by updating priority values
• Efficient algorithms can be implemented
• Included in real-time systems
• Disadvantages:
• High complexity
• High consumption of memory
• Less efficient data structure (compared to Heap, BST)
37
// Structure for the elements in the Priority Queue using Array
// priority queue
struct item { // Function to remove the element with
int value; // the highest priority
int priority; void dequeue()
}; {
// Find the position of the element with highest priority
int ind = peek();

void enqueue(int value, int priority) // Shift the element one index before from the position of
{ the element with highest priority is found
// Increase the size for (int i = ind; i < size; i++) {
size++; pr[i] = pr[i + 1];
}
// Insert the element
pr[size].value = value; // Decrease the size of the
pr[size].priority = priority; // priority queue by one
} size--;
Time Complexity: O(1) }

Time Complexity: O(n)

38
typedef struct node {
int data; Priority Queue using Linked List
// Lower values indicate higher priority
// Function to push according to priority
int priority;
void enqueue(Node** head, int d, int p)
struct node* next;
{
} Node;
Node* start = (*head);
// Create new Node
// Function to create a new node Node* temp = newNode(d, p);
Node* newNode(int d, int p)
{ // Special Case: The head of list has lesser priority than new node
Node* temp = (Node*)malloc(sizeof(Node)); if ((*head)->priority < p) {
temp->data = d; // Insert New Node before head
temp->priority = p; temp->next = *head;
temp->next = NULL; (*head) = temp;
}
return temp; else {
} // Traverse the list and find a position to insert new node
while (start->next != NULL && start->next->priority > p) {
// Removes the element with the start = start->next;
// highest priority form the list }
void dequeue(Node** head) // Either at the ends of the list or at required position
{ temp->next = start->next;
Node* temp = *head; start->next = temp;
(*head) = (*head)->next; }
free(temp); } Time Complexity: O(n)
} 39
Time Complexity: O(1)
Double Ended Queue (Deque)

• Special type of queue where insertion and deletion operations are performed from both ends
• There are two types of deque:
• Input restricted queue
• Output restricted queue

40
Application of Queue
Queue is used when things don’t have to be processed immediately, but have to be processed in
First In First Out order. This property of Queue makes it also useful in following kind of scenarios.

1) When a resource is shared among multiple consumers. Examples include CPU scheduling, Disk
Scheduling.
2) When data is transferred asynchronously (data not necessarily received at same rate as sent)
between two processes. Examples include IO Buffers, pipes, file IO, etc.
Problems on Stack and Queue
1. Implement the stack using Array/Linked List in C.
2. Reverse a string by using stack in C.
3. Converting infix expression to postfix (or prefix) expression using stack. (Example: input: A *
B + C * D and output: AB*CD*+)
4. Evaluation of arithmetic expression using stack. (Example input: (2+4)*(4+6) and output: 60 )
5. Implement the queue using Array in C
6. Implement priority queue
7. Implement stack using priority queue

42

You might also like