0% found this document useful (0 votes)
2 views54 pages

UNIT-2

The document provides an overview of data structures, focusing on stacks and queues, including their basic operations, implementations, and applications. It explains the importance of data structures in programming for efficiency, memory management, and solving complex problems. Additionally, it classifies data structures into primitive and non-primitive types, detailing linear and non-linear structures, and includes algorithms for stack operations implemented in C programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views54 pages

UNIT-2

The document provides an overview of data structures, focusing on stacks and queues, including their basic operations, implementations, and applications. It explains the importance of data structures in programming for efficiency, memory management, and solving complex problems. Additionally, it classifies data structures into primitive and non-primitive types, detailing linear and non-linear structures, and includes algorithms for stack operations implemented in C programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 54

UNIT-II

Stacks: Introduction to Data Structures: Basic Stack Operations


pop, push, display, delete, Representation of a Stack, Implementation of stack using Arrays, Stack
Applications: Recursion, Infix to postfix Transformation, Evaluating Postfix Expressions.
Queues: Basic Queues Operations enqueue, dequeue, Representation of a Queue using array, Impl
ementation of Queue Operations using arrays, Applications of Queues, Circular Queues.

Why We need Data Structures:


Data structures in C programming are like organizers for your data. Imagine you're organizing
your school supplies. If you just throw everything into one big box, it’ll be hard to find what you
need quickly. But if you use different organizers (like pencil cases, folders, etc.), it’s much easier
to find and use your supplies when you need them.
1. Efficiency
Data structures help in storing data in a way that allows for quick access and modifications. For
example, if you have a list of names, you can easily find, add, or remove names using the right
data structure.
2. Memory Management
Data structures allow you to use memory efficiently. For instance, arrays or linked lists let you
manage memory by allocating space only for the data you need.
3. Solving Complex Problems
Some problems require data to be organized in specific ways to be solved efficiently. For
example, if you need to manage tasks by priority, you can use a data structure like a priority
queue.
4. Reusability
Once you create a data structure, you can use it in distinct parts of your program or even in
different programs, saving you time and effort.
with the properties of data structures it helps us to understand data structures easily

Application of Data Structures:


● Sorting information

● Stacking information in order of importance


● Storing data effectively during latent periods
● Representing structural relationships of data
● Assessing graphical representations of data networks
● Building association arrays
● Removing and inserting information quickly.

CLASSIFICATION OF DATA STRUCTURES:


Primitive Data Structures
1. Primitive Data Structures are the data structures consisting of the numbers and the
characters that come in-built into programs.
2. These data structures can be manipulated or operated directly by machine-level
instructions.
3. Basic data types like Integer, Float, Character, and Boolean come under the Primitive Data
Structures.
4. These data types are also called Simple data types, as they contain characters that can't be
divided further
Non-Primitive Data Structures
1. Non-Primitive Data Structures are those data structures derived from Primitive Data
Structures.
2. These data structures can't be manipulated or operated directly by machine-level
instructions.
3. The focus of these data structures is on forming a set of data elements that is
either homogeneous (same data type) or heterogeneous (different data types).
4. Based on the structure and arrangement of data, we can divide these data structures into
two sub-categories –
i) Linear Data Structures
ii)Non-Linear Data Structures
Based on memory allocation, the Linear Data Structures are further classified into two types:
1. Static Data Structure: A static data structure is a collection of data that has a fixed size
and structure at compile time, and cannot be changed during runtime.
Examples:
Array. An array stores a collection of items at adjoining memory locations. Items that
are the same type are stored together so the position of each element can be calculated
or retrieved easily by an index. Arrays can be fixed or flexible in length.
Stack. A stack stores a collection of items in the linear order that operations are
applied. This order could be last in, first out (LIFO) or first in, first out (FIFO).
Queue. A queue stores a collection of items like a stack. However, the operation
order can only be FIFO.
2. Dynamic Data Structure :In Dynamic data structure the size of the structure is not fixed
and can be modified during the operations performed on it. Dynamic data structures are
designed to facilitate change of data structures in the run time.

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.

An everyday analogy of a stack data structure is a stack of books on a desk, Stack of


plates, table tennis, Stack of bootless, Undo or Redo mechanism in the Text Editors, etc.

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.

The following diagram depicts a stack and its operations:


A stack can be implemented by means of Array, Structure, Pointer, and Linked List. Stack
can either be a fixed size one or it may have a sense of dynamic resizing. Here, we are
going to implement stack using arrays, which makes it a fixed size stack implementation.

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

● push() Pushing (storing) an element on the stack.

● pop() Removing (accessing) an element from the stack.

When data is Pushed onto stack.

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

Implementation of peek() function in C programming language

int peek() {
return stack[top];
}

begin procedure
isfull():
Algorithm of isfull() function

begin procedure

if top less than


1

return
true else
return
false endif
end

Implementation of isfull() function in C programming language

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 1 Checks if the stack is full.

● Step 2 If the stack is full, produces an error and exit.

● 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.

● Step 5 Returns success.

If the linked list is used to implement the stack, then in step 3, we need to allocate space
dynamically.

Algorithm for PUSH Operation


A simple algorithm for Push operation can be derived as follows

begin procedure push: stack,


data

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 2 If the stack is empty, produces an error and exit

● Step 3 If the stack is not empty, access the data element at which the top is pointing.

● Step 4 Decreases the value of top by 1.

● Step 5 Returns success.

Algorithm for Pop Operation


A simple algorithm for Pop operation can be derived as follows

begin procedure pop:


stack

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
}

For a complete stack program in C programming language

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

Element at top of the stack:


15
Elements:
15
12
1
9
5
3
Stack full: false
Stack empty:
true
Representation of Stack:
1.Stack using Arrays

2.Stack using Linked List

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


PUSH OPERATION DIAGRAMATICAL REPRESENTATION:

Pop Operation in Stack:


Removes an item from the stack. The items are popped in the reversed order in which they are pushed. If
the stack is empty, then it is said to be an Underflow condition.
Algorithm for Pop Operation:

● 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().

POP OPERATION DIAGRAMATICAL REPRESENATION:

Source code for stack operations, using array:

#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

STACK OPERATIONS USING ARRAY


--------------------------------
1.PUSH
2.POP
3.DISPLAY
4.EXIT
Enter the Choice:1
Enter a value to be pushed:12

Enter the Choice:1


Enter a value to be pushed:24

Enter the Choice:1


Enter a value to be pushed:98

Enter the Choice:3

The elements in STACK


98
24
12
Press Next Choice
Enter the Choice:2

The popped elements is 98


Enter the Choice:3

The elements in STACK

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

1. Evaluation of Arithmetic Expressions


A stack is a very effective data structure for evaluating arithmetic expressions in programming
languages. An arithmetic expression consists of operands and operators.
The postfix notation places the operator after the operands. This notation is just the reverse of
Polish notation and also known as Reverse Polish notation.
Example: AB +, CD+, etc.
All these expressions are in postfix notation because the operator comes after the operands.

Evaluating Postfix expression:


Stack is the ideal data structure to evaluate the postfix expression because the top element is
always the most recent operand. The next element on the Stack is the second most recent
operand to be operated on.
Before evaluating the postfix expression, the following conditions must be checked. If any one
of the conditions fails, the postfix expression is invalid.
o When an operator encounters the scanning process, the Stack must contain a pair of
operands or intermediate results previously calculated.
o When an expression has been completely evaluated, the Stack must contain exactly one
value.

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

Procedure to convert from infix expression to postfix expression is as follows:

1.Scan the infix expression from left to right.


2.If the scanned symbol is left parenthesis, push it onto the stack.
If the scanned symbol is an operand, then place directly in the postfix expression (output).
3.If the symbol scanned is a right parenthesis, then go on popping all the items from the stack
and place them in the postfix expression till we get the matching left parenthesis.
4.If the scanned symbol is an operator, then go on removing all the operators from the stack and
place them in the postfix expression, if and only if the precedence of the operator which is on the
top of the stack is greater than (or greater than or equal) to the precedence of the scanned
operator and push the scanned operator onto the stack otherwise, push the scanned operator onto
the stack.

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.

SYMBO POSTFIX STRING STACK REMARKS


L
A A
+ A +
( A +(
B AB +(
* AB +(*
C ABC +(*
- ABC* +(-
( ABC* +(-(
D ABC*D +(-(
/ ABC*D +(-(/
E ABC*DE +(-(/
ABC*DE +(-(/
F ABC*DEF +(-(/
) ABC*DEF / +(-
* ABC*DEF / +(-*
G ABC*DEF /G +(-*
) ABC*DEF /G*- +
* ABC*DEF /G*- +*
H ABC*DEF /G*-H +*
End of ABC*DEF /G*-H*+ The input is now empty. Pop the output
string symbols from the stack until it is empty.

Convert a + b * c + (d * e + f) * g the infix expression into postfix form.


SYMBOL POSTFIX STRING STACK R
E
M
A
R
K
S
a a

+ 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.

//Source Code To Convert Infix to Postfix:


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX 100
char stack[MAX];
int top = -1;
void push(char item) {
if (top >= MAX - 1) {
printf("Stack Overflow\n");
} else {
stack[++top] = item;
}
}

char pop() {
if (top < 0) {
printf("Stack Underflow\n");
return -1;
} else {
return stack[top--];
}l
}

int precedence(char symbol) {


switch (symbol) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
default:
return 0;
}
}
int isOperator(char symbol) {
return (symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/' || symbol == '^');
}

void infixToPostfix(char infix[], char postfix[]) {


int i = 0, j = 0;
char item, x;

while ((item = infix[i++]) != '\0') {


if (isalnum(item)) {
postfix[j++] = item;
} else if (item == '(') {
push(item);
} else if (item == ')') {
while ((x = pop()) != '(') {
postfix[j++] = x;
}
} else if (isOperator(item)) {
while (top != -1 && precedence(stack[top]) >= precedence(item)) {
postfix[j++] = pop();
}
push(item);
}
}

while (top != -1) {


postfix[j++] = pop();
}

postfix[j] = '\0';
}

int main() {
char infix[MAX], postfix[MAX];

printf("Enter infix expression: ");


gets(infix);

infixToPostfix(infix, postfix);

printf("Postfix expression: %s\n", postfix);

return 0;
}

Evaluating Postfix Expression:


Postfix evaluation algorithm is a simple algorithm that allows us to evaluate postfix expressions.
The algorithm uses a stack to keep track of operands and performs arithmetic operations when an
operator is encountered. The algorithm can be summarized in the following steps:
First of all, it will Create an empty stack.
After that, it Scan the expression from left to right.
If an operand is encountered, it push it onto the stack.
If an operator is encountered, pop the top two operands from the stack, perform the operation,
and push the result back onto the stack.
After that, it Continue scanning the expression until all tokens have been processed.
When the expression has been fully scanned, the result will be the top element of the stack.

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.

Complete C program for postfix evaluation is given below:

#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;

while (symbol != '\0') {


if (symbol >= '0' && symbol <= '9') {
int num = symbol - '0';
push(num);
}
else if (is_operator(symbol)) {
operand2 = pop();
operand1 = pop();
switch(symbol) {
case '+': result = operand1 + operand2; break;
case '-': result = operand1 - operand2; break;
case '*': result = operand1 * operand2; break;
case '/': result = operand1 / operand2; break;
}
push(result);
}
i++;
symbol = expression[i];
}
result = pop();
return 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.

What is a Recursive Algorithm?

A recursive algorithm is an algorithm that uses recursion to solve a problem. Recursive


algorithms typically have two parts:
1. Base case: Which is a condition that stops the recursion.
2. Recursive case: Which is a call to the function itself with a smaller version of the
problem.
Types of Recursion:

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

● enqueue() add (store) an item to the queue.

● dequeue() remove (access) an item from the queue.

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.

● isfull() Checks if the queue is full.

● isempty() Checks if the queue is empty.

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

if front is less than MIN OR front is greater than


rear

return true
else
return
false endif

end
procedure

return
false
end

end
procedure

Implementation of isfull() function in C programming language –

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() {

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


Implementation of enqueue() in C programming language

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

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 :

Static implementation of Queue is represented by arrays. If Queue is implemented using


arrays, we must be sure about the exact number of elements we want to store in the queue,
because we have to declare the size of the array at design time or before the processing
starts. In this case, the beginning of the array will become the front for the queue and the last
location of the array will act as rear for the queue. Figure shows the representation of a
queue as an array.

Queue Using Array C Program


#include<stdio.h>
#include<stdlib.h>
#define maxsize 5
void insert();
void delete();
void display();
int front = -1, rear = -1;
int queue[maxsize];
void main ()
{
int choice;
while(choice != 4)
{
printf("\n*************************Main Menu*****************************\n");
printf("\n=========================================================\n");
printf("\n1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
printf("\nEnter your choice ?");
scanf("%d",&choice);
switch(choice)
{
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
display();
break;
case 4:
exit(0);
break;
default:
printf("\nEnter valid choice??\n");
}
}
}
void insert()
{
int item;
printf("\nEnter the element\n");
scanf("\n%d",&item);
if(rear == maxsize-1)
{
printf("\nOVERFLOW\n");
return;
}
if(front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear+1;
}
queue[rear] = item;
printf("\nValue inserted ");

}
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

Enter your choice ?1

Enter the element


123

Value inserted

*************Main Menu**************

==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?1

Enter the element


90

Value inserted

*************Main Menu**************

===================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?2

value deleted

*************Main Menu**************
==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?3

printing values .....

90

*************Main Menu**************

==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?4

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.

2.Signaling queue full: even if the queue is having vacant position.

For example, let us consider a linear queue status as follows:

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.

Representation of Circular Queue:


Circular Queue
Now, if we insert an element to the circular queue, as COUNT = MAX we cannot add
the element to circular queue. So, the circular queue is full.

Circular Queue Representation Using Arrays:

Operations On Circular Queue:


o Front: It is used to get the front element from the Queue.
o Rear: It is used to get the rear element from the Queue.
o enQueue(value): This function is used to insert the new value in the Queue. The new
element is always inserted from the rear end.
o deQueue(): This function deletes an element from the Queue. The deletion in a
Queue always takes place from the front end.
Applications of Circular Queue
The circular Queue can be used in the following scenarios:
o Memory management: The circular queue provides memory management. As we
have already seen that in linear queue, the memory is not managed very efficiently.
But in case of a circular queue, the memory is managed efficiently by placing the
elements in a location which is unused.
o CPU Scheduling: The operating system also uses the circular queue to insert the
processes and then execute them.
o Traffic system: In a computer-control traffic system, traffic light is one of the best
examples of the circular queue. Each light of traffic light gets ON one by one after
every jinterval of time. Like red light gets ON for one minute then yellow light for
one minute and then green light. After green light, the red light gets ON.
Enqueue operation

The steps of enqueue operation are given below:

o First, we will check whether the Queue is full or not.


o Initially the front and rear are set to -1. When we insert the first element in a Queue,
front and rear both are set to 0.
o When we insert a new element, the rear gets incremented,
o i.e., rear=rear+1.
There are two scenarios in which queue is not full:
1. If rear != max - 1, then rear will be incremented to mod(maxsize) and the new
value will be inserted at the rear end of the queue.
2. If front != 0 and rear = max - 1, it means that queue is not full, then set the value
of rear to 0 and insert the new element there.

There are two cases in which the element cannot be inserted:

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

while(choice<4 && choice!=0) // while loop


{
printf("\n Press 1: Insert an element");
printf("\nPress 2: Delete an element");
printf("\nPress 3: Display the element");
printf("\nEnter your choice");
scanf("%d", &choice);
switch(choice)
{

case 1:

printf("Enter the element which is to be inserted");


scanf("%d", &x);
enqueue(x);
break;
case 2:
dequeue();
break;
case 3:
display();

}}
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.

One-way List Representation of a Priority Queue :


One way to maintain a priority queue in memory is by means of a one-way list, as follows :
1. Each node in the list will contain three items of information; an information field
INFO, a priority number PRN and a link number LINK.
2. A node X precedes a node Y in the list
i. When X has higher priority then Y and
ii. When both have the same priority but X is added to the list before Y. This means
that the order in the one-way list corresponds to the order of the priority queue.
Priority queues will operate in the usual way : the lower the priority number, the higher the
priority.
Array representation of a Priority Queue:
Another way to maintain a priority queue in memory is to use a separate queue for each
level of priority (or for each priority number). Each such queue will appear in its own
circular array and must have its own pair of pointers, FRONT and REAR. In fact, each
queue is allocated the same amount of space, a two-dimensional array QUEUE can be
used instead of the linear arrays.
Out of these two ways of representing a Priority Queue, the array representation of a
priority queue is more time-efficient than the one way list. This is because when adding
an element to a one-way list, one must perform a linear search on the list. On the other
hand, the one-way list representation of the priority queue may be more space-efficient
than the array representation.

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 :

1. Round Robin technique for processor scheduling is implemented using queues.


2. All types of customer service (like railway ticket reservation ) center software’s
are designed using queues to store customers information.

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.

You might also like