Stacks in Data Structures is a linear type of data structure that follows the LIFO (Last-
In-First-Out) principle and allows insertion and deletion operations from one end of
the stack data structure, that is top. Implementation of the stack can be done by
contiguous memory which is an array, and non-contiguous memory which is a linked
list. Stack plays a vital role in many applications.
Introduction to Stack in Data Structures
The stack data structure is a linear data structure that accompanies a principle
known as LIFO (Last In First Out) or FILO (First In Last Out). Real-life examples of a
stack are a deck of cards, piles of books, piles of money, and many more.
This example allows you to perform operations from one end only, like when you
insert and remove new books from the top of the stack. It means insertion and
deletion in the stack data structure can be done only from the top of the stack. You
can access only the top of the stack at any given point in time.
● Inserting a new element in the stack is termed a push operation.
● Removing or deleting elements from the stack is termed pop operation.
Next, you will see how to represent the stacks in data structures in real-time.
Working of Stack in Data Structures
Now, assume that you have a stack of books.
You can only see the top, i.e., the top-most book, namely 40, which is kept top of the
stack.
If you want to insert a new book first, namely 50, you must update the top and then
insert a new text.
And if you want to access any other book other than the topmost book that is 40,
you first remove the topmost book from the stack, and then the top will point to the
next topmost book.
After working on the representation of stacks in data structures, you will see some
basic operations performed on the stacks in data structures.
Basic Operations on Stack in Data Structures
There following are some operations that are implemented on the stack.
Push Operation
Push operation involves inserting new elements in the stack. Since you have only one
end to insert a unique element on top of the stack, it inserts the new element at the
top of the stack.
Pop Operation
Pop operation refers to removing the element from the stack again since you have
only one end to do all top of the stack. So removing an element from the top of the
stack is termed pop operation.
Peek Operation
Peek operation refers to retrieving the topmost element in the stack without
removing it from the collections of data elements.
isFull()
isFull function is used to check whether or not a stack is empty.
isEmpty()
isEmpty function is used to check whether or not a stack is empty.
First, you will learn about the functions:
isFull()
The following is the algorithm of the isFull() function:
begin
If
top equals to maxsize
return true
else
return false
else if
end
The implementation of the isFull() function is as follows:
Bool isFull()
if(top == maxsize)
return true;
else
return false;
isEmpty()
The following is the algorithm of the isEmpty() function:
begin
If
topless than 1
return true
else
return false
else if
end
The implementation of the isEmpty() function is:
Bool isEmpty()
if(top = = -1)
return true;
else
return false;
Push Operation
Push operation includes various steps, which are as follows :
Step 1: First, check whether or not the stack is full
Step 2: If the stack is complete, then exit
Step 3: If not, increment the top by one
Step 4: Insert a new element where the top is pointing
Step 5: Success
The algorithm of the push operation is:
Begin push: stack, item
If the stack is complete, return null
end if
top ->top+1;
stack[top] <- item
end
This is how you implement a push operation:
if(! isFull ())
top = top + 1;
stack[top] = item;
}
else {
printf(“stack is full”);
Pop Operation
Step 1: First, check whether or not the stack is empty
Step 2: If the stack is empty, then exit
Step 3: If not, access the topmost data element
Step 4: Decrement the top by one
Step 5: Success
The following is the algorithm of the pop operation:
Begin pop: stack
If the stack is empty
return null
end if
item -> stack[top] ;
Top -> top - 1;
Return item;
end
Implementing a pop operation is as follows:
int pop( int item){
If isEmpty()) {
item = stack[top];
top = top - 1;
return item;
else{
printf(“stack if empty”);
}
Peek Operation
The algorithm of a peek operation is:
begin to peek
return stack[top];
end
The implementation of the peek operation is:
int peek()
return stack[top];
Implementation of Stack in Data Structures
You can perform the implementation of stacks in data structures using two data
structures that are an array and a linked list.
● Array: In array implementation, the stack is formed using an array. All the
operations are performed using arrays. You will see how all operations can be
implemented on the stack in data structures using an array data structure.
● Linked-List: Every new element is inserted as a top element in the linked list
implementation of stacks in data structures. That means every newly inserted
element is pointed to the top. Whenever you want to remove an element from
the stack, remove the node indicated by the top, by moving the top to its previous
node in the list.
Application of Stack in Data Structures
Here are the top 7 applications of the stack in data structure:
● Expression Evaluation and Conversion
● Backtracking
● Function Call
● Parentheses Checking
● String Reversal
● Syntax Parsing
● Memory Management
Now you will understand all the applications one at a time.
1. Expression Evaluation and Conversion
There are three types of expression that you use in programming, they are:
Infix Expression: An infix expression is a single letter or an operator preceded by one
single infix string followed by another single infix string.
● X
● X+Y
● (X + Y ) + (A - B)
Prefix Expression: A prefix expression is a single letter or an operator followed by two
prefix strings.
● X
● +XY
● ++XY-AB
Postfix Expression: A postfix expression (also called Reverse Polish Notation) is a
single letter or an operator preceded by two postfix strings.
● X
● XY+
● XY+CD-+
Similarly, the stack is used to evaluate these expressions and convert these
expressions like infix to prefix or infix to postfix.
2. Backtracking
Backtracking is a recursive algorithm mechanism that is used to solve optimization
problems.
To solve the optimization problem with backtracking, you have multiple solutions; it
does not matter if it is correct. While finding all the possible solutions in
backtracking, you store the previously calculated problems in the stack and use that
solution to resolve the following issues.
The N-queen problem is an example of backtracking, a recursive algorithm where the
stack is used to solve this problem.
3. Function Call
Whenever you call one function from another function in programming, the
reference of calling function stores in the stack. When the function call is terminated,
the program control moves back to the function call with the help of references
stored in the stack.
So stack plays an important role when you call a function from another function.
import java.util.Scanner;
class GetInputData
{
public static void main(String args[])
{
int num;
float fnum;
String str;
Scanner in = new Scanner(System.in);
//Get input String
System.out.println("Enter a string: ");
str = in.nextLine();
System.out.println("Input String is: "+str);
//Get input Integer
System.out.println("Enter an integer: ");
num = in.nextInt();
System.out.println("Input Integer is: "+num);
//Get input float number
System.out.println("Enter a float number: ");
fnum = in.nextFloat();
System.out.println("Input Float number is: "+fnum);
}
}
Output:
Enter a string:
Chaitanya
Input String is: Chaitanya
Enter an integer:
27
Input Integer is: 27
Enter a float number:
12.56
Input Float number is: 12.56
4. Parentheses Checking
Stack in data structures is used to check if the parentheses like ( ), { } are valid or not
in programing while matching opening and closing brackets are balanced or not.
So it stores all these parentheses in the stack and controls the flow of the program.
For e.g ((a + b) * (c + d)) is valid but {{a+b})) *(b+d}] is not valid.
5. String Reversal
Another exciting application of stack is string reversal. Each character of a string gets
stored in the stack.
The string's first character is held at the bottom of the stack, and the last character of
the string is held at the top of the stack, resulting in a reversed string after
performing the pop operation.
6. Syntax Parsing
Since many programming languages are context-free languages, the stack is used for
syntax parsing by many compilers.
7. Memory Management
Memory management is an essential feature of the operating system, so the stack is
heavily used to manage memory.
Queue
A queue is a linear data structure that stores the elements sequentially. It uses the FIFO
approach (First In First Out) for accessing elements. Queues are typically used to manage
threads in multithreading and implementing priority queuing systems. In this article, we will
learn about different types of queue data structure, basic operations performed on it,
implementation, and queue applications. A queue is an important data structure
in programming. A queue follows the FIFO (First In First Out) method and is open at both of
its ends. Data insertion is done at one end rear end or the tail of the queue while deletion is
done at the other end called the front end or the head of the queue.
Real Life example of a queue data structure
Let’s consider a line of people waiting to buy a ticket at a cinema hall. A new person will join
the line from the end and the person standing at the front will be the first to get the ticket
and leave the line. Similarly in a queue data structure, data added first will leave the queue
first.
Some other applications of the queue in real-life are:
● People on an escalator
● Cashier line in a store
● A car wash line
● One way exits
Operations performed on queue
The fundamental operations that can be performed on queue are listed as follows -
● Enqueue: The Enqueue operation is used to insert the element at the rear end of
the queue. It returns void.
● Dequeue: It performs the deletion from the front-end of the queue. It also
returns the element which has been removed from the front-end. It returns an
integer value.
● Peek: This is the third operation that returns the element, which is pointed by the
front pointer in the queue but does not delete it.
● Queue overflow (isfull): It shows the overflow condition when the queue is
completely full.
● Queue underflow (isempty): It shows the underflow condition when the Queue
is empty, i.e., no elements are in the Queue.
peek()
This function helps to see the data at the front of the queue. The algorithm of peek()
function is as follows −
Algorithm
begin procedure peek
return queue[front]
end procedure
Implementation of peek() function in C programming language −
Example
int peek() {
return queue[front];
isfull()
As we are using single dimension array to implement queue, we just check for the rear
pointer to reach at MAXSIZE to determine that the queue is full. In case we maintain the
queue in a circular linked-list, the algorithm will differ. Algorithm of isfull() function −
Algorithm
begin procedure isfull
if rear equals to MAXSIZE
return true
else
return false
endif
end procedure
Implementation of isfull() function in C programming language −
Example
bool isfull() {
if(rear == MAXSIZE - 1)
return true;
else
return false;
isempty():
As we are using single dimension array to implement queue, we just check for the rear
pointer to reach at MINSIZE to determine that the queue is empty. In case we maintain
the queue in a circular linked-list, the algorithm will differ.
Algorithm of isempty() function −
Algorithm
begin procedure isempty
if front is less than MIN OR front is greater than rear
return true
else
return false
endif
end procedure
If the value of front is less than MIN or 0, it tells that the queue is not yet initialized,
hence empty.
Here's the C programming code −
Example
bool isempty() {
if(front < 0 || front > rear)
return true;
else
return false;
Enqueue Operation
Queues maintain two data pointers, front and rear. Therefore, its operations are
comparatively difficult to implement than that of stacks.
The following steps should be taken to enqueue (insert) data into a queue −
Step 1 − Check if the queue is full.
Step 2 − If the queue is full, produce overflow error and exit.
Step 3 − If the queue is not full, increment rear pointer to point the next empty space.
Step 4 − Add data element to the queue location, where the rear is pointing.
Step 5 − return success.
Sometimes, we also check to see if a queue is initialized or not, to handle any
unforeseen situations.
Algorithm for enqueue operation
procedure enqueue(data)
if queue is full
return overflow
endif
rear ← rear + 1
queue[rear] ← data
return true
end procedure
Implementation of enqueue() in C programming language −
Example
int enqueue(int data)
if(isfull())
return 0;
rear = rear + 1;
queue[rear] = data;
return 1;
end procedure
Dequeue Operation
Accessing data from the queue is a process of two tasks − access the data where front is
pointing and remove the data after access. The following steps are taken to
perform dequeue operation −
Step 1 − Check if the queue is empty.
Step 2 − If the queue is empty, produce underflow error and exit.
Step 3 − If the queue is not empty, access the data where front is pointing.
Step 4 − Increment front pointer to point to the next available data element.
Step 5 − Return success.
Algorithm for dequeue operation
procedure dequeue
if queue is empty
return underflow
end if
data = queue[front]
front ← front + 1
return true
end procedure
Implementation of dequeue() in C programming language −
Example
int dequeue() {
if(isempty())
return 0;
int data = queue[front];
front = front + 1;
return data;
Types of Queues in Data Structure
There are four different types of queues in data structures:
● Simple Queue
● Circular Queue
● Priority Queue
● Double-Ended Queue (Deque)
Simple Queue
It is the most basic queue in which the insertion of an item is done at the front of the queue
and deletion takes place at the end of the queue.
● Ordered collection of comparable data kinds.
● Queue structure is FIFO (First in, First Out).
● When a new element is added, all elements added before the new element must be
deleted in order to remove the new element.
Circular Queue
A circular queue is a special case of a simple queue in which the last member is linked to the
first. As a result, a circle-like structure is formed.
● The last node is connected to the first node.
● Also known as a Ring Buffer as the nodes are connected end to end.
● Insertion takes place at the front of the queue and deletion at the end of the queue.
● Application of circular queue: Insertion of days in a week.
Now let’s take a look at the priority queue in the data structure.
Priority Queue
In a priority queue, the nodes will have some predefined priority in the priority queue. The
node with the least priority will be the first to be removed from the queue. Insertion takes
place in the order of arrival of the nodes.
Some of the applications of priority queue:
● Dijkstra’s shortest path algorithm
● Prim’s algorithm
● Data compression techniques like Huffman code
Below diagram shows how an application use priority queue for the items consumed by the
user.
Deque (Double Ended Queue)
In a double-ended queue, insertion and deletion can take place at both the front and rear
ends of the queue.
**queue** is a linear data structure that follows the First-In-First-Out (FIFO) principle. This
means that the first element added to the queue will be the first one to be removed. Think of it
like a line at a grocery store: the person who arrives first is the first one to be served.
Basic Operations of Queue Data Structure
● Enqueue (Insert): Adds an element to the rear of the queue.
● Dequeue (Delete): Removes and returns the element from the front of the queue.
● Peek: Returns the element at the front of the queue without removing it.
● Empty: Checks if the queue is empty.
● Full: Checks if the queue is full.
### Definition
A queue is defined as a collection of elements with two main operations:
1. **Enqueue**: Add an element to the end of the queue.
2. **Dequeue**: Remove an element from the front of the queue.
In addition to these, queues often support:
- **Peek/Front**: Retrieve the element at the front of the queue without removing it.
- **IsEmpty**: Check if the queue is empty.
- **Size**: Get the number of elements in the queue.
### Real-Time Examples
1. **Print Queue**: When multiple documents are sent to a printer, they are placed in a queue.
The first document sent is printed first, while the others wait their turn.
2. **Task Scheduling**: Operating systems use queues to manage processes. Tasks or processes
are added to a queue and executed in the order they arrive.
3. **Call Centers**: Incoming calls are placed in a queue and are attended to in the order they
are received.
### Applications
1. **Breadth-First Search (BFS)**: Queues are used in BFS algorithms to explore nodes in a
graph level by level.
2. **Task Scheduling**: Operating systems and real-time systems use queues to schedule tasks
and manage process execution.
3. **Buffering**: In data streaming or IO operations, queues are used to manage data being read
from or written to hardware devices.
4. **Handling Requests**: Web servers often use queues to manage incoming client requests,
processing them one by one.
### Operations
1. **Enqueue**: Insert an element at the end of the queue.
- **Time Complexity**: O(1)
2. **Dequeue**: Remove an element from the front of the queue.
- **Time Complexity**: O(1)
3. **Peek/Front**: Access the element at the front of the queue without removing it.
- **Time Complexity**: O(1)
4. **IsEmpty**: Check if the queue has no elements.
- **Time Complexity**: O(1)
5. **Size**: Get the number of elements in the queue.
- **Time Complexity**: O(1)
Queues can be implemented using arrays or linked lists, with each having its own advantages and
trade-offs depending on the context in which the queue is used.
#include <stdio.h>
#include <stdlib.h>
#define MAX 5 // Define maximum size of the queue
// Global variables
int front = 0;
int rear = -1;
int size = 0;
int arr[MAX];
// Function prototypes
int isEmpty();
int isFull();
void enqueue(int value);
int dequeue();
int peek();
int queueSize();
void displayQueue();
int main() {
int choice, value;
do {
printf("\nQueue Operations Menu:\n");
printf("1. Enqueue\n");
printf("2. Dequeue\n");
printf("3. Peek\n");
printf("4. Check if Empty\n");
printf("5. Check Size\n");
printf("6. Display Queue\n");
printf("7. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
if (isFull()) {
printf("Queue is full. Cannot enqueue.\n");
} else {
printf("Enter value to enqueue: ");
scanf("%d", &value);
enqueue(value);
printf("Enqueued %d\n", value);
}
break;
case 2:
if (isEmpty()) {
printf("Queue is empty. Cannot dequeue.\n");
} else {
value = dequeue();
printf("Dequeued %d\n", value);
}
break;
case 3:
if (isEmpty()) {
printf("Queue is empty. Nothing to peek.\n");
} else {
value = peek();
printf("Front element is %d\n", value);
}
break;
case 4:
if (isEmpty()) {
printf("Queue is empty.\n");
} else {
printf("Queue is not empty.\n");
}
break;
case 5:
printf("Queue size is %d\n", queueSize());
break;
case 6:
displayQueue();
break;
case 7:
printf("Exiting...\n");
break;
default:
printf("Invalid choice. Please try again.\n");
break;
}
} while (choice != 7);
return 0;
}
// Check if the queue is empty
int isEmpty() {
return (size == 0);
}
// Check if the queue is full
int isFull() {
return (size == MAX);
}
// Enqueue an element
void enqueue(int value) {
rear = (rear + 1) % MAX;
arr[rear] = value;
size++;
}
// Dequeue an element
int dequeue() {
int value = arr[front];
front = (front + 1) % MAX;
size--;
return value;
}
// Peek at the front element
int peek() {
return arr[front];
}
// Get the current size of the queue
int queueSize() {
return size;
}
// Display the elements of the queue
void displayQueue() {
if (isEmpty()) {
printf("Queue is empty.\n");
return;
}
printf("Queue elements: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[(front + i) % MAX]);
} printf("\n"); }
### Queue Operations Algorithms
#### 1. Initialize the Queue
**Algorithm:**
1. Set `front` to 0.
2. Set `rear` to -1.
3. Set `size` to 0.
4. Initialize the array `arr` of size `MAX`.
**Pseudo-code:**
InitializeQueue:
front = 0
rear = -1
size = 0
arr = array of size MAX
#### 2. Enqueue (Add an Element to the Queue)
**Algorithm:**
1. Check if the queue is full by comparing `size` with `MAX`.
- If full, print "Queue is full" and exit.
2. Increment `rear` by 1 and wrap around using modulo `MAX`.
3. Insert the new element at `arr[rear]`.
4. Increment `size` by 1.
**Pseudo-code:**
Enqueue(value):
if size == MAX:
print "Queue is full"
return
rear = (rear + 1) % MAX
arr[rear] = value
size = size + 1
#### 3. Dequeue (Remove an Element from the Queue)
**Algorithm:**
1. Check if the queue is empty by comparing `size` with 0.
- If empty, print "Queue is empty" and exit.
2. Retrieve the element from `arr[front]`.
3. Increment `front` by 1 and wrap around using modulo `MAX`.
4. Decrement `size` by 1.
5. Return the retrieved element.
**Pseudo-code:**
Dequeue():
if size == 0:
print "Queue is empty"
return
value = arr[front]
front = (front + 1) % MAX
size = size - 1
return value
#### 4. Peek (View the Front Element of the Queue)
**Algorithm:**
1. Check if the queue is empty by comparing `size` with 0.
- If empty, print "Queue is empty" and exit.
2. Retrieve and return the element at `arr[front]`.
**Pseudo-code:**
```plaintext
Peek():
if size == 0:
print "Queue is empty"
return
return arr[front]
#### 5. Check if the Queue is Empty
**Algorithm:**
1. Return `true` if `size` is 0; otherwise, return `false`.
**Pseudo-code:**
```plaintext
IsEmpty():
return (size == 0)
#### 6. Check if the Queue is Full
**Algorithm:**
1. Return `true` if `size` is equal to `MAX`; otherwise, return `false`.
**Pseudo-code:**
```plaintext
IsFull():
return (size == MAX)
#### 7. Get the Size of the Queue
**Algorithm:**
1. Return the current `size` of the queue.
**Pseudo-code:**
```plaintext
QueueSize():
return size
#### 8. Display the Queue Elements
**Algorithm:**
1. Check if the queue is empty by comparing `size` with 0.
- If empty, print "Queue is empty" and exit.
2. Iterate from `front` to `front + size - 1` (modulo `MAX`) and print each element.
**Pseudo-code:**
```plaintext
DisplayQueue():
if size == 0:
print "Queue is empty"
return
for i from 0 to size - 1:
index = (front + i) % MAX
print arr[index]