UNIT-2
UNIT-2
EX: Linked Lists, Stacks, and Queues are common examples of dynamic data structures.
Let's discuss each linear data structure in detail.
Array: An array consists of data elements of a same data type. For example, if we want to store
the roll numbers of 10 students, so instead of creating 10 integer type variables, we will create an
array having size 10. Therefore, we can say that an array saves a lot of memory and reduces the
length of the code.
Stack: It is linear data structure that uses the LIFO (Last In-First Out) rule in which the data
added last will be removed first. The addition of data element in a stack is known as a push
operation, and the deletion of data element form the list is known as pop operation.
Queue: It is a data structure that uses the FIFO rule (First In-First Out). In this rule, the element
which is added first will be removed first. There are two terms used in the queue front end
and rear The insertion operation performed at the back end is known ad enqueue, and the deletion
operation performed at the front end is known as dequeue.
Linked list: It is a collection of nodes that are made up of two parts, i.e., data elemengt and
reference to the next node in the sequence.
What is a Non-linear data structure?
1. A non-linear data structure is also another type of data structure in which the data
elements are not arranged in a contiguous manner.
2. As the arrangement is non sequential, so the data elements cannot be traversed or accessed
in a single run.
3. In the case of linear data structure, element is connected to two elements (previous and the
next element), whereas, in the non-linear data structure, an element can be connected to
more than two elements.
Tree:
It is a non-linear data structure that consists of various linked nodes. It has a hierarchical tree
structure that forms a parent-child relationship. The diagrammatic representation of a tree data
structure is shown below:
For example, the posts of employees are arranged in a tree data structure like managers, officers,
clerk. In the above figure, A represents a manager, B and C represent the officers, and other nodes
represent the clerks.
Graph
A graph is a non-linear data structure that has a finite number of vertices and edges, and these
edges are used to connect the vertices. The vertices are used to store the data elements, while the
edges represent the relationship between the vertices. A graph is used in various real-world
problems like telephone networks, circuit networks, social networks like LinkedIn, Facebook.
In the case of facebook, a single user can be considered as a node, and the connection of a user
with others is known as edges.
Stack
A Stack is a widely used linear data structure in modern computers in which insertions and
deletions of an element can occur only at one end, i.e., top of the Stack. It is used in all those
applications in which data must be stored and retrieved in the last.
This feature makes it LIFO data structure. LIFO stands for Last-in-first-out. Here, the
element which is placed (inserted or added) last, is accessed first. In stack terminology,
insertion operation is called PUSH operation and removal operation is called POP
operation.
Basic Operations:
Stack operations may involve initializing the stack, using it and then de-initializing it.
Apart from these basic stuffs, a stack is used for the following two primary operations
To use a stack efficiently, we need to check the status of stack as well. For the same purpose, the following fu
● peek() get the top data element of the stack, without removing it.
● isFull() check if stack is full.
● isEmpty() check if stack is empty.
At all times, we maintain a pointer to the last Pushed data on the stack. As this pointer
always represents the top of the stack, hence named top. The top pointer provides top value
of the stack without actually removing it.
First we should learn about procedures to support stack functions
Peek( ) function:
Algorithm for Peek( ) function:
begin procedure
return
end
int peek() {
return stack[top];
}
begin procedure
isfull():
Algorithm of isfull() function
begin procedure
return
true else
return
false endif
end
bool isfull() {
if(top ==
MAXSIZE)
return
true; else
return false;
isempty()
Algorithm of isempty() function
int pop(int data)
{
Implementation of isempty()
if(!isempty()) { function in C programming language is slightly different. We initialize top at -1
data =
stack[top]; top =
bool isempty()
top - 1; return
{ if(top == -1)
data;
return
}else {
true; else
printf("Could not retrieve data, Stack is
return false;
}
}
Push Operation
The process of putting a new data element onto stack is known as a Push Operation. Push operation involves
● Step 3 If the stack is not full, increments top to point next empty space.
● Step 4 Adds data element to the stack location, where top is pointing.
If the linked list is used to implement the stack, then in step 3, we need to allocate space
dynamically.
if stack is
full
return
null
top ← top +
1
stack[top] ←
data
Implementation
int of this
pop(int data) algorithm in C, is very easy. See the following code
{
void push(int{
if(!isempty())
data) { if(!
data =
isFull()) {
stack[top]; top =
top = top +
top - 1; return
1; stack[top] =
data;
data;
}else {
}else {
printf("Could not retrieve data, Stack is
printf("Could not insert data, Stack is full.\n");
}
Pop Operation
Accessing the content while removing it from the stack, is known as a Pop Operation. In an
array implementation of pop() operation, the data element is not actually removed, instead
top is decremented to a lower position in the stack to point to the next value. But in linked-
list implementation, pop() actually removes data element and deallocates memory space.
A Pop operation may involve the following steps
● Step 1 Checks if the stack is empty.
● Step 3 If the stack is not empty, access the data element at which the top is pointing.
if stack is
empty
return null
endif
data ←
stack[top]
top ← top -
int pop(int data)
{
if(!isempty())
Implementation of this{ algorithm in C, is as follows
data =
stack[top]; top =
top - 1; return
data;
}else {
printf("Could not retrieve data, Stack is
}
Stack Program in C
SImplementation in C
#include
int MAXSIZE = 8;
int stack[8];
int top = -1;
int isempty()
{if(top == -1)
return 1;
else
return 0;}
}
int isfull()
{
if(top==MAXSIZE
) return 1;
else
return 0;
}
int peek() {
return stack[top];
}
int pop()
{ int
data;
if(!isempty()) {
data =
stack[top]; top
= top - 1;
return data;
}else {
printf("Could not retrieve data, Stack is empty.\n");
}
}
int push(int data) {
if(!isfull())
{ top = top +
1;
stack[top]
=data; }else
{
printf("Could not insert data, Stack is full.\n");
}
}
If we compile and run the above program, it will produce the following result
Let us consider a stack with 6 elements capacity. This is called as the size of the stack. The
number of elements to be added should not exceed the maximum size of the stack. If we
attempt to add new element beyond the maximum size, we will encounter a stack overflow
condition. Similarly, you cannot remove elements beyond the base of the stack. If such is
the case, we will reach a stack underflow condition.
1.Stack using Arrays:
To implement a stack using an array, initialize an array and treat its end as the stack’s top.
Implement push (add to end), pop (remove from end), and peek (check end) operations, handling cases
for an empty or full stack.
Step-by-step approach:
1. Initialize an array to rep5resent the stack.
2. Use the end of the array to represent the top of the stack.
3. Implement push (add to end), pop (remove from the end), and peek (check end) operations,
ensuring to handle empty and full stack conditions.
Implement Stack Operations using Array:
Here are the following operations of implement stack using array:
Push Operation in Stack:
Adds an item to the stack. If the stack is full, then it is said to be an Overflow condition.
Algorithm for Push Operation:
● Before pushing the element to the stack, we check if the stack is full .
● If the stack is full (top == capacity-1) , then Stack Overflows and we cannot insert the
element to the stack.
● Otherwise, we increment the value of top by 1 (top = top + 1) and the new value is
inserted at top position .
● The elements can be pushed into the stack till we reach the capacity of the stack
●
●
● Before popping the element from the stack, we check if the stack is empty .
● If the stack is empty (top == -1), then Stack Underflows and we cannot remove any element
from the stack.
● Otherwise, we store the value at top, decrement the value of top by 1 (top = top – 1) and return
the stored top value.
●
When an element is taken off from the stack, the operation is performed by pop().
#include<stdio.h>
int stack[100],choice,n,top,x,i;
void push(void);
void pop(void);
void display(void);
int main()
{
top=-1;
printf("\n Enter the size of STACK[MAX=100]:");
scanf("%d",&n);
printf("\n\t STACK OPERATIONS USING ARRAY");
printf("\n\t--------------------------------");
printf("\n\t 1.PUSH\n\t 2.POP\n\t 3.DISPLAY\n\t 4.EXIT");
do
{
printf("\n Enter the Choice:");
scanf("%d",&choice);
switch(choice)
{
case 1:
{
push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
display();
break;
}
case 4:
{
printf("\n\t EXIT POINT ");
break;
}
default:
{
printf ("\n\t Please Enter a Valid Choice(1/2/3/4)");
}
}
}
while(choice!=4);
return 0;
}
void push()
{
if(top>=n-1)
{
printf("\n\tSTACK is over flow");
}
else
{
printf(" Enter a value to be pushed:");
scanf("%d",&x);
top++;
stack[top]=x;
}
}
void pop()
{
if(top<=-1)
{
printf("\n\t Stack is under flow");
}
else
{
printf("\n\t The popped elements is %d",stack[top]);
top--;
}
}
void display()
{
if(top>=0)
{
printf("\n The elements in STACK \n");
for(i=top; i>=0; i--)
printf("\n%d",stack[i]);
printf("\n Press Next Choice");
}
else
{
printf("\n The STACK is empty");
}
}
Copy
OUTPUT:
Enter the size of STACK[MAX=100]:10
24
12
Press Next Choice
Enter the Choice:4
EXIT POINT
Following is the various Applications of Stack in Data Structure:
o Evaluation of Arithmetic Expressions
o Backtracking
o Delimiter Checking
o Reverse a Data
o Processing Function Calls
Example:
Now let us consider the following infix expression 2 * (4+3) - 5.
Its equivalent postfix expression is 2 4 3 + * 5.
The three important features of postfix expression are:
1. The operands maintain the same order as in the equivalent infix expression.
2. The parentheses are not needed to designate the expression un-
ambiguously.
3. While evaluating the postfix expression the priority of the operators is no
longer relevant.
We consider five binary operations: +, -, *, / and $ or (exponentiation). For these
binary operations, the following in the order of precedence (highest to lowest):
Converting expressions Infix to Postfix using Stack:
Let us convert the expressions from one type to another. These can be done as follows:
1. Infix to postfix
Example 1:
Convert ((A (B + C)) * D) (E + F) infix expression to postfix form:
POSTFIX STRING STACK REMAR
KS
( (
( ((
A A ((
- A ((-
( A ((-(
B AB ((-(
AB ((-(+
C ABC ((-(+
) ABC+ ((-
) ABC+- (
ABC+- (*
D ABC+-D (*
) ABC+-D*
ABC+-D*
( ABC+-D* (
ABC+-D*E (
ABC+-D*E (+
ABC+-D*EF (+
) ABC+-D*EF+
End of The input is now empty. Pop the output symbols
string ABC+-D*EF+ from the stack until it is empty.
Example 2:
Convert the following infix expression A + (B * C (D / E F) * G) * H into its
equivalent postfix expression.
+ a +
b ab +
* ab +*
c abc +*
+ abc*+ +
( abc*+ +(
d abc*+d +(
* abc*+d +(*
e abc*+de +(*
+ abc*+de* +(+
f abc*+de*f +(+
) abc*+de*f+ +
* abc*+de*f+ +*
g abc*+de*f+g +*
End of The input is now empty. Pop the output symbols
string abc*+de*f+g*+ from the stack until it is empty.
char pop() {
if (top < 0) {
printf("Stack Underflow\n");
return -1;
} else {
return stack[top--];
}l
}
postfix[j] = '\0';
}
int main() {
char infix[MAX], postfix[MAX];
infixToPostfix(infix, postfix);
return 0;
}
Let's consider the expression "5 6 7 + * 8 -". We will evaluate this expression
using the postfix evaluation algorithm
1. Start scanning the expression from left to right.
2. Push operand 5 onto the stack.
3. Push operand 6 onto the stack.
4. Push operand 7 onto the stack.
5. Pop operands 7 and 6 from the stack, perform addition, and push the
result (13) back onto the stack.
6. Pop operands 13 and 5 from the stack, perform multiplication, and push the
result (65) back onto the stack.
7. Push operand 8 onto the stack.
8. Pop operands 8 and 65 from the stack, perform subtraction, and push the
result (57) back onto the stack.
9. The final result is 57.
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
// Stack implementation
int stack[MAX_SIZE];
int top = -1;
void push(int item) {
if (top >= MAX_SIZE - 1) {
printf("Stack Overflow\n");
return;
}
top++;
stack[top] = item;
}
int pop() {
if (top < 0) {
printf("Stack Underflow\n");
return -1;
}
int item = stack[top];
top--;
return item;
}
int is_operator(char symbol) {
if (symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/') {
return 1;
}
return 0;
}
int evaluate(char* expression) {
int i = 0;
char symbol = expression[i];
int operand1, operand2, result;
int main() {
char expression[] = "5 6 7 + * 8 -";
int result = evaluate(expression);
printf("Result= %d\n", result);
return 0;
}
What is Recursion?
Recursion is a programming technique where a function calls itself within its own definition.
This allows a function to break down a problem into smaller subproblems, which are then solved
recursively.
How Does Recursion Work?
Recursion works by creating a stack of function calls. When a function calls itself, a new
instance of the function is created and pushed onto the stack. This process continues until a base
case is reached, which is a condition that stops the recursion. Once the base case is reached, the
function calls start popping off the stack and returning their results.
There are several different recursion types and terms. These include:
1. Direct recursion: This is typified by the factorial implementation where the methods call
itself.
2. In-Direct recursion: This happens where one method, say method A, calls another
method B, which then calls method A. This involves two or more methods that
eventually create a circular call sequence.
3. Head recursion: The recursive call is made at the beginning of the method.
4. Tail recursion: The recursive call is the last statement.
Examples of Recursion
Here are some common examples of recursion:
Example 1: Factorial: The factorial of a number n is the product of all the integers from 1 to n.
The factorial of n can be defined recursively as:
factorial(n) = n * factorial(n-1)
Example 2: Fibonacci sequence: The Fibonacci sequence is a sequence of numbers where each
number is the sum of the two preceding numbers. The Fi ;
bonacci sequence can be defined recursively as:
fib(n) = fib(n-1) + fib(n-2)
Example to Understand How Recursion Uses Stack in C Language?
We already discussed that the memory is used by dividing it into three sections i.e. code section,
stack section, and heap section. We will take the following example and show you how the stack
is created and utilized as a recursive function.
As shown in the above example, we have two functions: fun1() and the main() function. The
machine code of these two functions will be there in the code section of the main memory. Let
us run the program and see how the stack is created.
The program execution starts from the main functions. Inside the main function, int x=3 is the
first statement that is the x variable created. Inside the stack, the activation record for the main
function is created, and it will have its own variable, that is, x having value 3.
The next statement is fun1(), i.e., call to the fun1 function. So, once the function fun1() is called,
it has just one variable: n. The activation record for that fun1() function is created, and it will
have that variable n, and the value x is passed to that n variable, so it will store the value 3. For a
better understanding, please have a look at the below image.
This is the first call of the fun1 function.
Let’s continue. Inside the fun1 function, first, it will check whether n is greater than 0. Yes, n (3)
is greater than 0, and the condition satisfies. So, it will print the value 3 and call the fun1()
function with the reduced value of n, i.e., n-1, i.e., 2. Once the fun1 function is called, again
another activation record for that function is created inside the stack. The variable n is again
created within this activation record with the value 2, as shown in the image below. This is the
second call of the fun1 function.
In the second call, first, it will check whether n is greater than 0. Yes, n (i.e., 2) is greater than 0,
and the condition satisfies. So, it will print the value 2 and call the fun1() function with the
reduced value of n, i.e., 2-1, i.e., 1. Once the fun1 function is called, another activation record for
that function is created, and the variable n is created with the value 1, as shown in the below
image. This is the third call of the fun1 function.
The third fun1 function call will check whether n is greater than 0. Yes, n (i.e., 1) is greater than
0. So, it will print the value 1, and again, it will call the fun1() function with the reduced value of
n, i.e., 1-1, i.e., 0. Once the fun1 function is called, another activation record for the fun1
function is created, and the variable n is created with the value 0, as shown in the below image.
Now, the fourth fun1 function call will check whether n is greater than 0. No, n (i.e., 0) is not
greater than 0. So, it will not come inside the condition and will not execute those two
statements; it simply comes out of the function. Once the fourth fun1 function call is completed,
it will delete that fourth fun1 activation area from the stack, as shown in the image below.
Once that function call is completed and once that activation record is deleted from the stack, the
control goes back to the previous function call i.e. fun1(1) i.e. the third function call. In the third
fun1 function call, there are no more operations to perform, so it simply comes out from that
function to the previous function call and deletes the activation record from the stack, as shown
in the below image, created for the third func
tion call.
Once that activation record is deleted from the stack, the control returns to the previous function
call, i.e., fun1(2), i.e., the second function call. In the second fun1 function call, there are no
more operations to perform, so it simply comes out from that function to the previous function
call and deletes the activation record from the stack created for the second function call, as
shown in the image below.
Once that activation record for the second function call is deleted from the stack, the control
returns to the previous function call, i.e., fun1(3), i.e., the first function call. In the first fun1
function call, there are no more operations to perform, so it simply comes out from that function
to the main function and deletes the activation record from the stack created for the first function
call, as shown in the image below.
Nothing is inside the main function after the fun1 function call, so it will also delete the
activation record created for the main function, as shown in the image below.
Queue:
Queue is an abstract data structure, somewhat similar to Stacks. Unlike stacks, a queue is
open at both its ends. One end is always used to insert data (enqueue) and the other is used to
remove data (dequeue). Queue follows First-In-First-Out methodology, i.e., the data item
stored first will be accessed first.
A real-world example of queue can be a single-lane one-way road, where the vehicle enters
first, exits first. More real-world examples can be seen as queues at the ticket windows and
bus-stops.
Queue Representation
As we now understand that in queue, we access both ends for different reasons. The following diagram given
As in stacks, a queue can also be implemented using Arrays, Linked-lists, Pointers and
Structures. For the sake of simplicity, we shall implement queues using one-dimensional
array.
Basic Operations:
Queue operations may involve initializing or defining the queue, utilizing it, and then completely erasing it fr
Few more functions are required to make the above-mentioned queue operation efficient.
These are
● peek() Gets the element at the front of the queue without removing it.
In queue, we always dequeue (or access) data, pointed by front pointer and while enqueing (or
storing) data in the queue we take help of rear pointer.
Let's first learn about supportive functions of a queue
peek()
This function helps to see the data at the front
of the queue. The algorithm of peek() function is as follows
begin procedure
peek
return
queue[front]
end
procedure
Implementation of peek() function in C programming language
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
begin procedure
isfull
if rear equals to
MAXSIZE
return true
begin
elseprocedure
isempty
return true
else
return
false endif
end
procedure
return
false
end
end
procedure
bool isfull() {
if(rear == MAXSIZE
- 1) return true;
else
return false;
}
isempty()
Algorithm of isempty() function
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
bool isempty() {
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 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.
Sometimes, we also check to see if a queue is initialized or not, to handle any unforeseen
situations.
int enqueue(int
data)
if(isfull())
return 0;
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 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.
data =
queue[front]
front ← front + 1
return
true
end procedure
Implementation of dequeue() in C programming language
int dequeue() {
if(isempty())
return 0;
int data =
queue[front]; front =
front + 1;
return
data;
}
Queue Implementation:
Static implementation :
}
void delete()
{
int item;
if (front == -1 || front > rear)
{
printf("\nUNDERFLOW\n");
return;
}
else
{
item = queue[front];
if(front == rear)
{
front = -1;
rear = -1 ;
}
else
{
front = front + 1;
}
printf("\nvalue deleted ");
}
void display()
{
int i;
if(rear == -1)
{
printf("\ n Empty queue\n");
}
else
{ printf("\n printing values .....\n");
for(i=front;i<=rear;i++)
{
printf("\n%d\n",queue[i]);
}
}
}
************Main Menu**************
==============================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Value inserted
*************Main Menu**************
==============================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Value inserted
*************Main Menu**************
===================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
value deleted
*************Main Menu**************
==============================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
90
*************Main Menu**************
==============================================
1.insert an element
2.Delete an element
3.Display the queue
4.Exit
Circular Queue:
A more efficient queue representation is obtained by regarding the array Q[MAX] as circular.
Any number of items could be placed on the queue. This implementation of a queue is called a
circular queue because it uses its storage array as if it were a circle instead of a linear list.
There are two problems associated with linear queue. They are:
1.Time consuming: linear time to be spent in shifting the elements to the beginning of the
queue.
3 44 55
3
0 1 2 3 4
REA R = 5
F R
Next insert another element, say 66 to the queue. We cannot insert 66 to the queue as the rear
crossed the maximum size of the queue (i.e., 5). There will be queue full signal. The queue
status is as follows:
3 44 55
3
0 1 2 3 4
REA R = 5
F R
This difficulty can be overcome if we treat queue position with index zero as a position that
comes after position with index four then we treat the queue as a circular queue.
In circular queue if we reach the end for inserting elements to it, it is possible to insert new
elements if the slots at the beginning of the circular queue are empty.
1.When front ==0 && rear = max-1, which means that front is at the first position of
the Queue and rear is at the last position of the Queue.
2.front== rear + 1;
//Program For Implementation of Circular Queue In C programming
#include <stdio.h>
# define max 6
int queue[max]; // array declaration
int front=-1;
int rear=-1;
// function to insert an element in a circular queue
void enqueue(int element)
{
if(front==-1 && rear==-1) // condition to check queue is empty
{
front=0;
rear=0;
queue[rear]=element;
}
else if((rear+1)%max==front) // condition to check queue is full
{
printf("Queue is overflow..");
}
else
{
rear=(rear+1)%max; // rear is incremented
queue[rear]=element; // assigning a value to the queue at the rear position.
}
}
// function to delete the element from the queue
int dequeue()
{
if((front==-1) && (rear==-1)) // condition to check queue is empty
{
printf("\nQueue is underflow..");
}
else if(front==rear)
{
printf("\nThe dequeued element is %d", queue[front]);
front=-1;
rear=-1;
}
else
{
printf("\nThe dequeued element is %d", queue[front]);
front=(front+1)%max;
}
}
// function to display the elements of a queue
void display()
{
int i=front;
if(front==-1 && rear==-1)
{
printf("\n Queue is empty..");
}
else
{
printf("\nElements in a Queue are :");
while(i<=rear)
{
printf("%d,", queue[i]);
i=(i+1)%max;
}
}
}
int main()
{
int choice=1,x; // variables declaration
case 1:
}}
return 0;
}
PRIORITY QUEUE
A priority queue is a collection of elements where the elements are stored according to
their priority levels. The order in which the elements get added or removed is decided by
the priority of the element.
Following rules are applied to maintain a priority queue :
1)The element with a higher priority is processed before any element of lower priority.
If there are elements with the same priority, then the element added first in the queue
would get processed.
2)Priority queues are used for implementing job scheduling by the operating system
where jobs with higher priorities are to be processed first. Another application of Priority
queues is simulation systems where priority corresponds to event times.
There are mainly two ways of maintaining a priority queue in memory. One uses a one-
way list, and the other uses multiple queues. The ease or difficultly in adding elements to
or deleting them from a priority queue clearly depends on the representation that one
chooses.
This is because in using the array representation overflow occurs when the number of
elements in any single priority level exceeds the capacity for that level, but in using the
one-way list, overflow occurs only when the total number of elements exceeds the total
capacity. Another alternative is to use a linked list for each priority level.
APPLICATIONS OF QUEUES :
Printer server routines are designed using queues. A number of users share a printer using
printer server ( a dedicated computer to which a printer is connected), the printer server
then spools all the jobs from all the users, to the server’s hard disk in a queue. From here
jobs are printed one-by-one according to their number in the queue.