Data Structures Through C-Notes (2023)
Data Structures Through C-Notes (2023)
UNIT – I: Introduction to Data Structures: Introduction to the Theory of Data Structures, Data
Representation, Abstract Data Types, Data Types, Primitive Data Types, Data Structure and Structured
Type, Atomic Type, Difference between Abstract Data Types, Data Types, and Data Structures, Refinement
Stages.
Principles of Programming and Analysis of Algorithms: Software Engineering, Program Design,
Algorithms, Different Approaches to Designing an Algorithm, Complexity, Big „O‟ Notation, Algorithm
Analysis, Structured Approach to Programming, Recursion, Tips and Techniques for Writing Programs in
“C”.
UNIT – II: Arrays: Introduction to Linear and Non- Linear Data Structures, One- Dimensional Arrays,
Array Operations, Two- Dimensional arrays, Multidimensional Arrays, Pointers and Arrays, an Overview of
Pointers
Linked Lists: Introduction to Lists and Linked Lists, Dynamic Memory Allocation, Basic Linked List
Operations, Doubly Linked List, Circular Linked List, Atomic Linked List, Linked List in Arrays, Linked
List versus Arrays
UNIT – III: Stacks: Introduction to Stacks, Stack as an Abstract Data Type, Representation of Stacks
through Arrays, Representation of Stacks through Linked Lists, Applications of Stacks, Stacks and
Recursion
Queues: Introduction, Queue as an Abstract data Type, Representation of Queues, Circular Queues, Double
Ended Queues- Deques, Priority Queues, Application of Queues
UNIT – IV: Binary Trees: Introduction to Non- Linear Data Structures, Introduction Binary Trees, Types
of Trees, Basic Definition of Binary Trees, Properties of Binary Trees, Representation of Binary Trees,
Operations on a Binary Search Tree, Binary Tree Traversal, Counting Number of Binary Trees, Applications
of Binary Tree
UNIT – V: Searching and sorting: Sorting – An Introduction, Bubble Sort, Insertion Sort, Merge Sort,
searching – An Introduction, Linear or Sequential Search, Binary Search, Indexed Sequential Search
Graphs: Introduction to Graphs, Terms Associated with Graphs, Sequential Representation of Graphs,
Linked Representation of Graphs, Traversal of Graphs, Spanning Trees, Shortest Path, Application of
Graphs.
We can organize this data as a record like Teacher record, which will have both teacher's name and age in
it. Now we can collect and store teacher's records in a file or database as a data structure.
In simple language, Data Structures are structures programmed to store ordered data, so that various
operations can be performed on it easily. It represents the knowledge of data to be organized in memory. It
should bedesigned and implemented in such a way that it reduces the complexity and increases the
efficiency.
*Linear Data Structures: A data structure is called linear if all of its elements are arranged in the
linear order. In linear data structures, the elements are stored in non-hierarchical way where each element
has the successors and predecessors except the first and last element.
Arrays: An array is a collection of similar type of data items and each data item is called an element of the
array. The data type of the element may be any valid data type like char, int, float or double.
The elements of array share the same variable name but each one carries a different index number known as
subscript. The array can be one dimensional, two dimensional or multidimensional.
Linked List: Linked list is a linear data structure which is used to maintain a list in the memory. It can be
seen as the collection of nodes stored at non-contiguous memory locations. Each node of the list contains a
pointer to its adjacent node.
Stack: Stack is a linear list in which insertion and deletions are allowed only at one end, called top.
A stack is an abstract data type (ADT), can be implemented in most of the programming languages. It is
named as stack because it behaves like a real-world stack, for example: - piles of plates or deck of cards etc.
It is an abstract data structure, similar to stack. Queue is opened at both end therefore it follows First-In-
First-Out (FIFO) methodology for storing the data items.
*Non Linear Data Structures: This data structure does not form a sequence i.e. each item or element
is connected with two or more other items in a non-linear arrangement. The data elements are not arranged
in sequential structure.
Trees: Trees are multilevel data structures with a hierarchical relationship among its elements known as
nodes. The bottommost nodes in the herierchy are called leaf node while the topmost node is called root
node. Each node contains pointers to point adjacent nodes.
Tree data structure is based on the parent-child relationship among the nodes. Each node in the tree can have
more than one children except the leaf nodes whereas each node can have atmost one parent except the root
node. Trees can be classified into many categories which will be discussed later in this tutorial.
Graphs: Graphs can be defined as the pictorial representation of the set of elements (represented by
vertices) connected by the links known as edges. A graph is different from tree in the sense that a graph can
have cycle while the tree can not have the one.
The data structures can also be classified on the basis of the following characteristics:
Characteristics Description
In Linear data structures, the data items are
Linear
arranged in a linear sequence. Example: Array
In Non-Linear data structures, the data items are not
Non-Linear
in sequence. Example: Tree, Graph
In homogeneous data structures, all the elements
Homogeneous
are of same type. Example: Array
In Non-Homogeneous data structure, the elements
Non-Homogeneous may or may not be of the same type.
Example: Structures
Static data structures are those whose sizes and
Static structures associated memory locations are fixed, at
compile time. Example: Array
Dynamic structures are those which expands or
shrinks depending upon the program need and its
Dynamic execution. Also, their associated memory locations
changes. Example: Linked List created using
pointers
Primitive data structure is a kind of data Non-primitive data structure is a type of data
structure that stores the data of only one structure that can store the data of more than one
type. type.
Examples of primitive data structure are Examples of non-primitive data structure are Array,
integer, character, float. Linked list, stack.
Primitive data structure will contain some Non-primitive data structure can consist of a NULL
value, i.e., it cannot be NULL. value.
The size depends on the type of the data In case of non-primitive data structure, size is not
structure. fixed.
Primitive data structure can be used to call Non-primitive data structure cannot be used to call
the methods. the methods.
BOOLEAN Has only two possible values. TRUE and FALSE. TRUE
Abstraction: It is a technique of hiding the internal details from the user and only
showing the necessary details to the user.
DIFFERENCE BETWEEN ABSTRACT DATA TYPES, DATA TYPES, AND DATA STRUCTURES
REFINEMENTSTAGES
The best approach to solve a complex problem is to divide it into smaller parts such thateach part
becomes an independent module which is easy to manage. An example of thisapproach is the System
Development Life Cycle (SDLC) methodology.
This helps in understanding the problem, analyzing solutions, and handling the problemsefficiently.
PROGRAM DESIGN
For a single-user application (used by one person at a time), which normally reads data, saves it in a data
structure, computes on the data, and writes the results, there is a standard way of organizing the component
structure, data structure, and control structure:
1. First, design the program's component structure with three components, organized in a model-view-
controller pattern.
2. Next, decide what form of data structure (array, table, set, list, tree, etc.) will hold the program's
data. The data structure will be inserted in the program's model component.
3. Then, write the algorithm that defines the execution steps --- the control structure. The algorithm
will be placed inside the program's controller.
4. Determine the form of input and output (disk file, typed text in a command window, dialogs, a
graphical-use interface, etc.) that the program uses. This will be embedded in the program's view.
Once the four-step design is finished, then it is time to convert the design into coding.
ALGORITHMS(imp)
Algorithm is a step-by-step procedure, which defines a set of instructions to be executed in a certain order to
get the desired output.
Characteristics of an Algorithm
Not all procedures can be called an algorithm. An algorithm should have the following characteristics −
Unambiguous − Algorithm should be clear and unambiguous. Each of its steps (or phases), and
their inputs/outputs should be clear and must lead to only one meaning.
Input − An algorithm should have 0 or more well-defined inputs.
Output − An algorithm should have 1 or more well-defined outputs, and should match the desired
output.
Finiteness − Algorithms must terminate after a finite number of steps.
Feasibility − Should be feasible with the available resources.
Independent − An algorithm should have step-by-step directions, which should be independent of
any programming code.
1. Implementation Method
Lvr Degree College
2. Design Method
3. Other Classifications
1.Classification by Implementation Method: There are primarily three main categories into which an
algorithm can be named in this type of classification. They are:
1. Recursion or Iteration: A recursive algorithm is an algorithm which calls itself again and
again until a base condition is achieved whereas iterative algorithms use loops and/or data
structures like stacks, queues to solve any problem. Every recursive solution can be
implemented as an iterative solution and vice versa.
Example: The Tower of Hanoi is implemented in a recursive fashion while Stock
Span problem is implemented iteratively.
2. Exact or Approximate: Algorithms that are capable of finding an optimal solution for any
problem are known as the exact algorithm. For all those problems, where it is not possible to
find the most optimized solution, an approximation algorithm is used. Approximate algorithms
are the type of algorithms that find the result as an average outcome of sub outcomes to a
problem.
Example: For NP-Hard Problems, approximation algorithms are used. Sorting algorithms are
the exact algorithms.
3. Serial or Parallel or Distributed Algorithms: In serial algorithms, one instruction is executed
at a time while parallel algorithms are those in which we divide the problem into subproblems
and execute them on different processors. If parallel algorithms are distributed on different
machines, then they are known as distributed algorithms.
2.Classification by Design Method: There are primarily three main categories into which an algorithm
can be named in this type of classification. They are:
1. Greedy Method: In the greedy method, at each step, a decision is made to choose the local
optimum, without thinking about the future consequences.
Example: Fractional Knapsack, Activity Selection, Dijkstra algorithm.
2. Divide and Conquer: The Divide and Conquer strategy involves dividing the problem into
sub-problem, recursively solving them, and then recombining them for the final answer.
Example: Merge sort, Quicksort.
3. Dynamic Programming: The approach of Dynamic programming is similar to divide and
conquer. The difference is that whenever we have recursive function calls with the same result,
instead of calling them again we try to store the result in a data structure in the form of a table
and retrieve the results from the table. Thus, the overall time complexity is reduced.
“Dynamic” means we dynamically decide, whether to call a function or retrieve values from
the table.
Example: 0-1 Knapsack, subset-sum problem .
4. Linear Programming: In Linear Programming, there are inequalities in terms of inputs and
maximizing or minimizing some linear functions of inputs.
Example: Maximum flow of Directed Graph
5. Reduction(Transform and Conquer): In this method, we solve a difficult problem by
transforming it into a known problem for which we have an optimal solution. Basically, the
goal is to find a reducing algorithm whose complexity is not dominated by the resulting
reduced algorithms.
Example: Selection algorithm for finding the median in a list involves first sorting the list and
then finding out the middle element in the sorted list. These techniques are also
called transform and conquer.
3.Other Classifications : Apart from classifying the algorithms into the above broad categories, the
algorithm can be classified into other broad categories like:
The term algorithm complexity measures how many steps are required by the algorithm to solve the given
problem.
The complexity of an algorithm computes the amount of time and spaces required by an algorithm for an
input of size (n). The complexity of an algorithm can be divided into two types. The time complexity and
the space complexity.
1.Time complexity
2.Space complexity
1.Time Complexity
Time Complexity of an algorithm is the representation of the amount of time required by the algorithm to
execute to completion. Time requirements can be denoted or defined as a numerical function t(N), where
t(N) can be measured as the number of steps, provided each step takes constant time.
For example, in case of addition of two n-bit integers, N steps are taken. Consequently, the total
computational time is t(N) = c*n, where c is the time consumed for addition of two bits. Here, we observe
that t(N) grows linearly as input size increases.
2.Space Complexity
Space complexity of an algorithm represents the amount of memory space needed the algorithm in its life
cycle.
Space needed by an algorithm is equal to the sum of the following two components
A fixed part that is a space required to store certain data and variables (i.e., simple variables and constants,
program size etc.), that are not dependent of the size of the problem.
A variable part is a space required by variables, whose size is totally dependent on the size of the problem.
For example, recursion stack space, dynamic memory allocation etc.
Big-O Notation(imp)
Big-O notation represents the upper bound of the running time of an algorithm. Thus, it gives the
worst-case complexity of an algorithm.
Lvr Degree College
O(g(n))={f(n):there exists positive constants c and n 0 such that 0<=f(n)<=cg(n) for all n >=n 0}
The above expression can be described as a function f(n) belongs to the set O(g(n)) if there exists a positive
constant c such that it lies between 0 and cg(n) , for sufficiently large n .
For any value of n , the running time of an algorithm does not cross the time provided by O(g(n)) .
Since it gives the worst-case running time of an algorithm, it is widely used to analyse an algorithm as we
are always interested in the worst-case scenario.
Big O complexity can be visualized with this graph:
As a programmer first and a mathematician second (or maybe third or last) here the best way to understand
Big O thoroughly examples in code. So, below are some common orders of growth along with descriptions
and examples where possible.
1.O(1)
void printFirstElementOfArray(int arr[])
{
printf("First element of array = %d",arr[0]);
}
This function runs in O(1) time (or "constant time") relative to its input. The input array could be 1 item or
1,000 items, but this function would still just require one step.
2. O(n)
void printAllElementOfArray(int arr[], int size)
{
for (int i = 0; i < size; i++)
{
printf("%d\n", arr[i]);
}
}
This function runs in O(n) time (or "linear time"), where n is the number of items in the array. If the array
has 10 items, we have to print 10 times. If it has 1000 items, we have to print 1000 times.
3. O(n2)
void printAllPossibleOrderedPairs(int arr[], int size)
Lvr Degree College
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
printf("%d = %d\n", arr[i], arr[j]);
}
}
}
Here we're nesting two loops. If our array has n items, our outer loop runs n times and our inner loop runs n
times for each iteration of the outer loop, giving us n2 total prints. Thus this function runs in O(n2) time (or
"quadratic time"). If the array has 10 items, we have to print 100 times. If it has 1000 items, we have to print
1000000 times.
4. O(2n)
int fibonacci(int num)
{
if (num <= 1) return num;
return fibonacci(num - 2) + fibonacci(num - 1);
}
An example of an O(2n) function is the recursive calculation of Fibonacci numbers. O(2n) denotes an
algorithm whose growth doubles with each addition to the input data set. The growth curve of an O(2n)
function is exponential - starting off very shallow, then rising meteorically.
Algorithm Analysis
the efficiency or running time of an algorithm is stated as a function relating the input length to the number of steps,
known as time complexity, or volume of memory, known as space complexity.
Analysis of algorithm is the process of analysing the problem-solving capability of the algorithm in terms
of the time and size required (the size of memory for storage while implementation). However, the main
concern of analysis of algorithms is the required time or performance. Generally, we perform the following
types of analysis −
Worst-case − The maximum number of steps taken on any instance of size a.
Best-case − The minimum number of steps taken on any instance of size a.
Average case − An average number of steps taken on any instance of size a.
Amortized − A sequence of operations applied to the input of size a averaged over time.
To solve a problem, we need to consider time as well as space complexity as the program may run on a
system where memory is limited but adequate space is available or may be vice-versa. In this context, if we
compare bubble sort and merge sort. Bubble sort does not require additional memory, but merge sort
requires additional space. Though time complexity of bubble sort is higher compared to merge sort, we
may need to apply bubble sort if the program needs to run in an environment, where memory is very
limited.
Recursion:
Recursion is one of the most powerful tools in a programming language.
When function is called within the same function, it is known as recursion in C.
The function which calls the same function, is known as recursive function.
Recursion is defined as defining anything in terms of itself. Recursion is used to
solve problems involving iterations, in reverse order.
Types of Recursion
There are two types of Recursion
Direct Recursion
When in the body of a method there is a call to the same method, we say that the
method is directly recursive.
There are three types of Direct Recursion
Linear Recursion
Lvr Degree College
Binary Recursion
Multiple Recursion
Linear Recursion
Linear recursion begins by testing for a set of base cases there should be at least one.
Perform a single recursive call. This recursive step may involve a test that decides
which of several possible recursive calls to make, but it should ultimately choose to
make just one of these calls each time we perform this step.
Define each possible recursion call, so that it makes progress towards a base case.
Binary Recursion
Binary recursion occurs whenever there are two recursive calls for each non base case.
Multiple Recursion
In multiple recursion we make not just one or two but many recursive calls.
Polish notation(imp)
Postfix notation is the useful form of intermediate code if the given language is
expressions.Postfix notation is also called as 'suffix notation' and 'reverse
polish'.Postfix notation is a linear representation of a syntax tree.In the postfix
notation, any expression can be written unambiguously without parentheses.The
ordinary (infix) way of writing the sum of x and y is with operator in the middle: x *
y. But in the postfix notation, we place the operator at the right end as xy *.In
postfix notation, the operator follows the operand.
Example:
Polish notation: * + 3 2 – 5 1
When used as the syntax for programming language interpreters, Polish notation can be
readily parsed into an abstract syntax tree and stored in a stack. In traditional infix notation
with brackets, the equation has to be parsed, the brackets removed, and the operator and
operands repositioned. This is not the case with Polish notation, which is why LISP(LIST
PROCESSING) and other related languages use this notation to define their syntax.
UNIT-II
What is an Array? Explain about Arrays (imp)
Representation of an array
We can represent an array in various ways in different programming languages. declaration
of array in C language -
Example:
#include <stdio.h>
void main() {
int Arr[5] = {18, 30, 15, 70, 12};
int i;
printf("Elements of the array are:\n");
for(i = 0; i<5; i++) {
printf("Arr[%d] = %d, ", i, Arr[i]);
}
}
Output:
Time Complexity
Space Complexity
Disadvantages of Array
o Array is homogenous. It means that the elements with similar data type can be stored in it.
o In array, there is static memory allocation that is size of an array cannot be altered.
o There will be wastage of memory if we store less number of elements than the declared size.
A linked list is a linear data structure, in which the elements are not stored at contiguous memory locations. The
elements in a linked list are linked using pointers. Linked list can be defined as the nodes that are randomly
stored in the memory. A node in the linked list contains two parts, i.e., first is the data part and second
is the address part. The last node of the list contains a pointer to the null.
Linked list can be represented as the connection of nodes in which each node points to the next node
of the list. The representation of the linked list is shown below –
o Singly-linked list - Singly linked list can be defined as the collection of an ordered set of
elements. A node in the singly linked list consists of two parts: data part and link part. Data part
of the node stores actual information that is to be represented by the node, while the link part of
the node stores the address of its immediate successor.
o Doubly linked list - Doubly linked list is a complex type of linked list in which a node contains
a pointer to the previous as well as the next node in the sequence. Therefore, in a doubly-linked
list, a node consists of three parts: node data, pointer to the next node in sequence (next
pointer), and pointer to the previous node (previous pointer).
o Circular singly linked list - In a circular singly linked list, the last node of the list contains a
pointer to the first node of the list. We can have circular singly linked list as well as circular
doubly linked list.
o Circular doubly linked list - Circular doubly linked list is a more complex type of data
structure in which a node contains pointers to its previous node as well as the next node.
Circular doubly linked list doesn't contain NULL in any of the nodes. The last node of the list
contains the address of the first node of the list. The first node of the list also contains the
address of the last node in its previous pointer.
void create();
void display();
void insert_begin();
void insert_end();
void insert_pos();
void delete_begin();
void delete_end();
void delete_pos();
struct node
{
int info;
struct node *next;
};
struct node *start=NULL;
int main()
{
int choice;
while(1){
case 9:
exit(0);
break;
default:
printf("n Wrong Choice:n");
break;
}
}
return 0;
}
void create()
{
struct node *temp,*ptr;
temp=(struct node *)malloc(sizeof(struct node));
if(temp==NULL)
{
printf("nOut of Memory Space:n");
exit(0);
}
printf("nEnter the data value for the node:t");
scanf("%d",&temp->info);
temp->next=NULL;
if(start==NULL)
{
start=temp;
}
else
{
ptr=start;
while(ptr->next!=NULL)
{
ptr=ptr->next;
}
ptr->next=temp;
}
}
void display()
{
struct node *ptr;
if(start==NULL)
{
printf("nList is empty:n");
return;
}
else
{
ptr=start;
printf("nThe List elements are:n");
Lvr Degree College
while(ptr!=NULL)
{
printf("%dt",ptr->info );
ptr=ptr->next ;
}
}
}
void insert_begin()
{
struct node *temp;
temp=(struct node *)malloc(sizeof(struct node));
if(temp==NULL)
{
printf("nOut of Memory Space:n");
return;
}
printf("nEnter the data value for the node:t" );
scanf("%d",&temp->info);
temp->next =NULL;
if(start==NULL)
{
start=temp;
}
else
{
temp->next=start;
start=temp;
}
}
void insert_end()
{
struct node *temp,*ptr;
temp=(struct node *)malloc(sizeof(struct node));
if(temp==NULL)
{
printf("nOut of Memory Space:n");
return;
}
printf("nEnter the data value for the node:t" );
scanf("%d",&temp->info );
temp->next =NULL;
if(start==NULL)
{
start=temp;
}
else
{
ptr=start;
while(ptr->next !=NULL)
{
ptr=ptr->next ;
}
ptr->next =temp;
}
}
void insert_pos()
Lvr Degree College
{
struct node *ptr,*temp;
int i,pos;
temp=(struct node *)malloc(sizeof(struct node));
if(temp==NULL)
{
printf("nOut of Memory Space:n");
return;
}
printf("nEnter the position for the new node to be inserted:t");
scanf("%d",&pos);
printf("nEnter the data value of the node:t");
scanf("%d",&temp->info) ;
temp->next=NULL;
if(pos==0)
{
temp->next=start;
start=temp;
}
else
{
for(i=0,ptr=start;i<pos-1;i++) { ptr=ptr->next;
if(ptr==NULL)
{
printf("nPosition not found:[Handle with care]n");
return;
}
}
temp->next =ptr->next ;
ptr->next=temp;
}
}
void delete_begin()
{
struct node *ptr;
if(ptr==NULL)
{
printf("nList is Empty:n");
return;
}
else
{
ptr=start;
start=start->next ;
printf("nThe deleted element is :%dt",ptr->info);
free(ptr);
}
}
void delete_end()
{
struct node *temp,*ptr;
if(start==NULL)
{
printf("nList is Empty:");
exit(0);
Lvr Degree College
}
else if(start->next ==NULL)
{
ptr=start;
start=NULL;
printf("nThe deleted element is:%dt",ptr->info);
free(ptr);
}
else
{
ptr=start;
while(ptr->next!=NULL)
{
temp=ptr;
ptr=ptr->next;
}
temp->next=NULL;
printf("nThe deleted element is:%dt",ptr->info);
free(ptr);
}
}
void delete_pos()
{
int i,pos;
struct node *temp,*ptr;
if(start==NULL)
{
printf("nThe List is Empty:n");
exit(0);
}
else
{
printf("nEnter the position of the node to be deleted:t");
scanf("%d",&pos);
if(pos==0)
{
ptr=start;
start=start->next ;
printf("nThe deleted element is:%dt",ptr->info );
free(ptr);
}
else
{
ptr=start;
for(i=0;i<pos;i++) { temp=ptr; ptr=ptr->next ;
if(ptr==NULL)
{
printf("nPosition not Found:n");
return;
}
}
temp->next =ptr->next ;
printf("nThe deleted element is:%dt",ptr->info );
free(ptr);
}
}
Lvr Degree College
}
Array elements store in a contiguous Linked list elements can be stored anywhere
memory location. in the memory or randomly stored.
Array works with a static memory. Here The Linked list works with dynamic memory.
static memory means that the memory Here, dynamic memory means that the
size is fixed and cannot be changed at the memory size can be changed at the run
run time. time according to our requirements.
Array elements are independent of each Linked list elements are dependent on each
other. other. As each node contains the address of
the next node so to access the next node,
we need to access its previous node.
Array takes more time while performing Linked list takes less time while performing
any operation like insertion, deletion, etc. any operation like insertion, deletion, etc.
Unit-III
Representation of Stack:
It follows the principle LIFO (Last In First Out) in which the insertion and deletion take place from
one side known as a top. In stack, we can insert the elements of a similar data type, i.e., the different
data type elements cannot be inserted in the same stack. The two operations are performed in LIFO,
i.e., push and pop operation.
The following are the operations that can be performed on the stack:
o push(x): It is an operation in which the elements are inserted at the top of the stack. In
the push function, we need to pass an element which we want to insert in a stack.
o pop(): It is an operation in which the elements are deleted from the top of the stack. In
the pop() function, we do not have to pass any argument.
o peek()/top(): This function returns the value of the topmost element available in the stack. Like
pop(), it returns the value of the topmost element but does not remove that element from the
stack.
o isEmpty(): If the stack is empty, then this function will return a true value or else it will return a
false value.
o isFull(): If the stack is full, then this function will return a true value or else it will return a false
value.
2.pop algorithm
3.peek algorithm
1. START
2. return the element at the top of the stack
3. END
4.isFull algorithm
1. START
2. If the size of the stack is equal to the top position of the stack, the stack is full.
Return 1.
3. Otherwise, return 0.
4. END
5.isEmpty algorithm
1. START
2. If the top value is -1, the stack is empty. Return 1.
3. Otherwise, return 0.
4. END
void push ()
{
int val;
if (top == n )
printf("\n Overflow");
else
{
printf("Enter the value?");
scanf("%d",&val);
top = top +1;
stack[top] = val;
}
}
void pop ()
{
if(top == -1)
printf("Underflow");
else
top = top -1;
}
void show()
{
for (i=top;i>=0;i--)
A Queue is defined as a linear data structure that is open at both ends and the operations are performed in
First In First Out (FIFO) order. Hence, it follows FIFO (First-In-First-Out) structure, i.e. the data item
inserted first will also be accessed first. The data is inserted into the queue through one end and deleted from
it using the other end.
representation
a queue ADT can also be implemented using arrays, linked lists, or pointers. we implement queues using a one-
dimensional array.
1 − START
2 – Check if the queue is full.
3 − If the queue is full, produce overflow error and exit.
4 − If the queue is not full, increment rear pointer to point the next empty space.
5 − Add data element to the queue location, where the rear is pointing.
6 − return success.
7 – END
1 – START
2 − Check if the queue is empty.
3 − If the queue is empty, produce underflow error and exit.
4 − If the queue is not empty, access the data where front is pointing.
5 − Increment front pointer to point to the next available data element.
6 − Return success.
7 – END
1 – START
2 – Return the element at the front of the queue
3 – END
Algorithm
1 – START
2 – If the count of queue elements equals the queue size, return true
3 – Otherwise, return false
4 – END
Algorithm
1 – START
2 – If the count of queue elements equals zero, return true
3 – Otherwise, return false
4 – END
#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("Main Menu\n");
printf("\n1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
}
void delete()
{
int item;
if (front == -1 || front > rear)
{
printf("\nUNDERFLOW\n");
return;
}
else
void display()
{
int i;
if(rear == -1)
{
printf("\nEmpty queue\n");
}
else
{ printf("\nprinting values .....\n");
for(i=front;i<=rear;i++)
{
printf("\n%d\n",queue[i]);
}
}
}
1. Task Scheduling: Queues can be used to schedule tasks based on priority or the order in which they
were received.
2. Resource Allocation: Queues can be used to manage and allocate resources, such as printers or CPU
processing time.
3. Batch Processing: Queues can be used to handle batch processing jobs, such as data analysis or image
rendering.
4. Message Buffering: Queues can be used to buffer messages in communication systems, such as
message queues in messaging systems or buffers in computer networks.
5. Event Handling: Queues can be used to handle events in event-driven systems, such as GUI
applications or simulation systems.
6. Traffic Management: Queues can be used to manage traffic flow in transportation systems, such as
airport control systems or road networks.
7. Operating systems: Operating systems often use queues to manage processes and resources. For
example, a process scheduler might use a queue to manage the order in which processes are executed.
8. Network protocols: Network protocols like TCP and UDP use queues to manage packets that are
transmitted over the network. Queues can help to ensure that packets are delivered in the correct order
and at the appropriate rate.
9. Breadth-first search algorithm: The breadth-first search algorithm uses a queue to explore nodes in a
graph level-by-level. The algorithm starts at a given node, adds its neighbors to the queue, and then
processes each neighbor in turn.
1. Simple Queue
2. Circular Queue
3. Priority Queue
4. Double Ended Queue
1.Simple queue
In a simple queue, insertion takes place at the rear and removal occurs at the front. It strictly follows the
FIFO (First in First out) rule.
2. Circular Queue
In a circular queue, the last element points to the first element making a circular link.
The main advantage of a circular queue over a simple queue is better memory utilization. If the last position
is full and the first position is empty, we can insert an element in the first position. This action is not possible
in a simple queue.
3.priority queue:
A priority queue is a special type of queue in which each element is associated with a priority and is served
according to its priority. If elements with the same priority occur, they are served according to their order in
the queue.
A binary search tree in a data structure is typically used to represent or store hierarchical data. A “binary
tree” is a tree data structure where every node has two child nodes (at the most) that form the tree branches.
These child nodes are called left and right child nodes.
The full binary tree is also known as a strict binary tree. The tree can only be considered as the full
binary tree if each node must contain either 0 or 2 children. The full binary tree can also be defined as
the tree in which each node must contain 2 children except the leaf nodes.
The above tree is a complete binary tree because all the nodes are completely filled, and all the nodes
in the last level are added at the left first.
A tree is a perfect binary tree if all the internal nodes have 2 children, and all the leaf nodes are at the
same level.
The degenerate binary tree is a tree in which all the internal nodes have only one children.
Lvr Degree College
The above tree is a degenerate binary tree because all the nodes have only one child. It is also known
as a right-skewed tree as all the nodes have a right child only.
The balanced binary tree is a tree in which both the left and right trees differ by atmost 1. For
example, AVL and Red-Black trees are balanced binary tree.
1. In-order Traversal
2. Pre-order Traversal
3. Post-order Traversal
1. in-order traversal:
In this traversal method, the left subtree is visited first, then the root and later the right sub-tree. We should always
remember that every node may represent a subtree itself.
If a binary tree is traversed in-order, the output will produce sorted key values in an ascending order.
Algorithm
Until all nodes are traversed −
Step 1 − Recursively traverse left subtree.
Step 2 − Visit root node.
Step 3 − Recursively traverse right subtree.
2. Pre-order Traversal
In this traversal method, the root node is visited first, then the left subtree and finally the right subtree.
We start from A, and following pre-order traversal, we first visit A itself and then move to its left subtree B. B is also
traversed pre-order. The process goes on until all the nodes are visited. The output of pre-order traversal of this tree
will be −
A→B→D→E→C→F→G
Algorithm
Until all nodes are traversed −
3.Post-order Traversal
In this traversal method, the root node is visited last, hence the name. First we traverse the left subtree, then the right
subtree and finally the root node.
We start from A, and following pre-order traversal, we first visit the left subtree B. B is also traversed post-order. The
process goes on until all the nodes are visited. The output of post-order traversal of this tree will be −
D→E→B→F→G→C→A
Algorithm
Until all nodes are traversed −
Step 1 − Recursively traverse left subtree.
Step 2 − Recursively traverse right subtree.
Step 3 − Visit root node.
PART-A
SEARCHING TECHNIQUES
1.What is Searching?
Searching Techniques
To search an element in a given array, it can be done in following ways:
1. Sequential Search
2. Binary Search
The above figure shows how sequential search works. It searches an element or value from an array till the
desired element or value is not found. If we search the element 25, it will go step by step in a sequence
order. It searches in a sequence order. Sequential search is applied on the unsorted or unordered list when
there are fewer elements in a list.
Algorithm:
Linear_Search(a, n, val) // 'a' is the given array, 'n' is the size of given array, 'val' is the value to search
Step 1: set pos = -1
Step 2: set i = 1
Step 3: repeat step 4 while i <= n
Step 4: if a[i] == val
set pos = i
print pos
go to step 6
[end of if]
Lvr Degree College
set ii = i + 1
[end of loop]
Step 5: if pos = -1
print "value is not present in the array "
[end of if]
Step 6: exit
Output:
Binary searching starts with middle element. If the element is equal to the element that we are searching
then return true. If the element is less than then move to the right of the list or if the element is greater
than then move to the left of the list. Repeat this, till you find an element.
Algorithm:
Binary_Search(a, lower_bound, upper_bound, val) // 'a' is the given array, 'lower_bound' is the index of
the first array element, 'upper_bound' is the index of the last array element, 'val' is the value to search
#include <stdio.h>
Lvr Degree College
int binarySearch(int a[], int beg, int end, int val)
{
int mid;
if(end >= beg)
{ mid = (beg + end)/2;
/* if the item to be searched is present at middle */
if(a[mid] == val)
{
return mid+1;
}
/* if the item to be searched is smaller than middle, then it can only be in left subarray */
else if(a[mid] < val)
{
return binarySearch(a, mid+1, end, val);
}
/* if the item to be searched is greater than middle, then it can only be in right subarray */
else
{
return binarySearch(a, beg, mid-1, val);
}
}
return -1;
}
int main() {
int a[] = {11, 14, 25, 30, 40, 41, 52, 57, 70}; // given array
int val = 40; // value to be searched
int n = sizeof(a) / sizeof(a[0]); // size of array
int res = binarySearch(a, 0, n-1, val); // Store result
printf("The elements of the array are - ");
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\nElement to be searched is - %d", val);
if (res == -1)
printf("\nElement is not present in the array");
else
printf("\nElement is present at %d position of array", res);
return 0;
}
Output
PART-A
Sorting refers to arranging data in a particular format. Sorting algorithm specifies the way to arrange data in
a particular order. Sorting is the process of arranging the elements of an array so that they can be placed
either in ascending or descending order.
SN Sorting Description
Algorithms
1 Bubble Sort It is the simplest sort method which performs sorting by repeatedly
moving the largest element to the highest index of the array. It
comprises of comparing each element to its adjacent element and
replace them accordingly.
2 Insertion As the name suggests, insertion sort inserts each element of the array
Sort to its proper place. It is a very simple sort method which is used to
arrange the deck of cards while playing bridge.
3 Merge Sort Merge sort follows divide and conquer approach in which, the list is
first divided into the sets of equal elements and then each half of the
list is sorted by using merge sort. The sorted list is combined again to
form an elementary sorted array.
4 Quick Sort Quick sort is the most optimized sort algorithms which performs
sorting in O(n log n) comparisons. Like Merge sort, quick sort also
work by using divide and conquer approach.
5 Selection Selection sort finds the smallest element in the array and place it on
Sort the first place on the list, then it finds the second smallest element in
the array and place it on the second place. This process continues
until all the elements are moved to their correct ordering. It carries
running time O(n2) which is worst than insertion sort.
Algorithm
In the algorithm given below, suppose arr is an array of n elements. The assumed swap function in the
algorithm will swap the values of given array elements.
begin BubbleSort(arr)
for all array elements
if arr[i] > arr[i+1]
swap(arr[i], arr[i+1])
end if
end for
return arr
end BubbleSort
Working example
To understand the working of bubble sort algorithm, let's take an unsorted array. We are taking a short and accurate
array, as we know the complexity of bubble sort is O(n2).
Let the elements of array are -
First Pass
Sorting will start from the initial two elements. Let compare them to check which is greater.
Here, 32 is greater than 13 (32 > 13), so it is already sorted. Now, compare 32 with 26.
Here, 26 is smaller than 36. So, swapping is required. After swapping new array will look like -
Here, 35 is greater than 32. So, there is no swapping required as they are already sorted.
Now, the comparison will be in between 35 and 10.
Here, 10 is smaller than 32. So, swapping is required. After swapping, the array will be -
Here, 10 is smaller than 26. So, swapping is required. After swapping, the array will be -
Output:
Here, 31 is greater than 12. That means both elements are already in ascending order. So, for now, 12 is stored in a
sorted sub-array.
Here, 25 is smaller than 31. So, 31 is not at correct position. Now, swap 31 with 25. Along with swapping, insertion
sort will also check it with all elements in the sorted array.
For now, the sorted array has only one element, i.e. 12. So, 25 is greater than 12. Hence, the sorted array remains
sorted after swapping.
Now, two elements in the sorted array are 12 and 25. Move forward to the next elements that are 31 and 8.
Now, the sorted array has three items that are 8, 12 and 25. Move to the next items that are 31 and 32.
Hence, they are already sorted. Now, the sorted array includes 8, 12, 25 and 31.
Lvr Degree College
Move to the next elements that are 32 and 17.
#include <stdio.h>
void insert(int a[], int n) /* function to sort an aay with insertion sort */
{
int i, j, temp;
for (i = 1; i < n; i++)
{
temp = a[i];
j = i - 1;
while(j>=0 && temp <= a[j]) /* Move the elements greater than temp to one position ahead from their
current position*/
{
a[j+1] = a[j];
j = j-1;
}
a[j+1] = temp;
}
}
int main()
{
int a[] = { 12, 31, 25, 8, 32, 17 };
int n = sizeof(a) / sizeof(a[0]);
Lvr Degree College
printf("Before sorting array elements are - \n");
printArr(a, n);
insert(a, n);
printf("\nAfter sorting array elements are - \n");
printArr(a, n);
return 0;
}
Output:
The sub-lists are divided again and again into halves until the list cannot be divided further. Then we
combine the pair of one element lists into two-element lists, sorting them in the process. The sorted
two-element pairs is merged into the four-element lists, and so on until we get the sorted list.
Algorithm
In the following algorithm, arr is the given array, beg is the starting element, and end is the last
element of the array.
Working Example
To understand the working of the merge sort algorithm, let's take an unsorted array. It will be easier to
understand the merge sort via an example.
As there are eight elements in the given array, so it is divided into two arrays of size 4.
Now, again divide these two arrays into halves. As they are of size 4, so divide them into new arrays of
size 2.
Now, again divide these arrays to get the atomic value that cannot be further divided.
In combining, first compare the element of each array and then combine them into another array in
sorted order.
So, first compare 12 and 31, both are in sorted positions. Then compare 25 and 8, and in the list of two
values, put 8 first followed by 25. Then compare 32 and 17, sort them and put 17 first followed by 32.
After that, compare 40 and 42, and place them sequentially.
In the next iteration of combining, now compare the arrays with two data values and merge them into
an array of found values in sorted order.
Now, there is a final merging of the arrays. After the final merging of above arrays, the array will look
like
#include <stdio.h>
/* Function to merge the subarrays of a[] */
void merge(int a[], int beg, int mid, int end)
{
int i, j, k;
int n1 = mid - beg + 1;
while (j<n2)
{
a[k] = RightArray[j];
j++;
k++;
}
}
int main()
{
int a[] = { 12, 31, 25, 8, 32, 17, 40, 42 };
int n = sizeof(a) / sizeof(a[0]);
printf("Before sorting array elements are - \n");
printArray(a, n);
mergeSort(a, 0, n - 1);
printf("After sorting array elements are - \n");
printArray(a, n);
return 0;
}
Output
Algorithm
QUICKSORT (array A, start, end)
{
1 if (start < end)
2{
3 p = partition(A, start, end)
4 QUICKSORT (A, start, p - 1)
5 QUICKSORT (A, p + 1, end)
6}
}
Working example
To understand the working of quick sort, let's take an unsorted array. It will make the concept more clear
and understandable.
Let the elements of array are -
Now, a[pivot] < a[right], so algorithm moves forward one position towards left, i.e. -
Because, a[pivot] > a[right], so, algorithm will swap a[pivot] with a[right], and pivot moves to right, as -
Now, a[left] = 19, a[right] = 24, and a[pivot] = 24. Since, pivot is at right, so algorithm starts from left and
moves to right.
As a[pivot] > a[left], so algorithm moves one position to right as -
Now, a[left] = 9, a[right] = 24, and a[pivot] = 24. As a[pivot] > a[left], so algorithm moves one position to
right as -
Now, a[left] = 29, a[right] = 24, and a[pivot] = 24. As a[pivot] < a[left], so, swap a[pivot] and a[left], now
pivot is at left, i.e. -
Since, pivot is at left, so algorithm starts from right, and move to left. Now, a[left] = 24, a[right] = 29, and
a[pivot] = 24. As a[pivot] < a[right], so algorithm moves one position to left, as -
Now, a[pivot] = 24, a[left] = 14, and a[right] = 24. Pivot is at right, so the algorithm starts from left and
move to right.
Now, a[pivot] = 24, a[left] = 24, and a[right] = 24. So, pivot, left and right are pointing the same element. It
represents the termination of procedure.
Element 24, which is the pivot element is placed at its exact position.
Elements that are right side of element 24 are greater than it, and the elements that are left side of element 24
are smaller than it.
Now, in a similar manner, quick sort algorithm is separately applied to the left and right sub-arrays. After
sorting gets done, the array will be -
OUTPUT:
PART-B
GRAPHS
1. Define graph? Explain about types of Graph (IMP)
A Graph is a non-linear data structure consisting of vertices and edges. A graph is represented as G = {V, E}, where G
is the graph space, V is the set of vertices are sometimes also referred to as nodes and the E is the edges are lines or
arcs that connect any two nodes in the graph.
More formally a Graph is composed of a set of vertices( V ) and a set of edges( E ). The graph is denoted by G(V,E).
1. directed graph:
In a directed graph, edges form an ordered pair. Edges represent a specific path from some vertex A to another
vertex B. Node A is called initial node while node B is called terminal node.
A directed graph is shown in the following figure.
2. Undirected graph:
in an undirected graph, edges are not associated with the directions with them. An undirected graph is
shown in the above figure since its edges are not attached with any of the directions. If an edge exists
between vertex A and B then the vertices can be traversed from B to A as well as A to B.
4.Finite Graphs
A graph is said to be finite if it has a finite number of vertices and a finite number of edges.
5. Infinite Graph:
A graph is said to be infinite if it has an infinite number of vertices as well as an infinite number of edges.
Closed Path:A path will be called as closed path if the initial node is same as terminal node. A path will
be closed path if V0=VN.
Simple Path:If all the nodes of the graph are distinct with an exception V 0=VN, then such path P is called
as closed simple path.
Cycle:A cycle can be defined as the path which has no repeated edges or vertices except the first and
last vertices.
Complete Graph:A complete graph is the one in which every node is connected with all other nodes. A
complete graph contain n(n-1)/2 edges where n is the number of nodes in the graph.
Weighted Graph:In a weighted graph, each edge is assigned with some data such as length or weight.
The weight of an edge e can be given as w(e) which must be a positive (+) value indicating the cost of
traversing the edge.
Digraph:A digraph is a directed graph in which each edge of the graph is associated with some direction
and the traversing can be done only in the specified direction.
Loop:An edge that is associated with the similar end points can be called as Loop.
Adjacent Nodes:If two nodes u and v are connected via an edge e, then the nodes u and v are called as
neighbours or adjacent nodes.
Degree of the Node:A degree of a node is the number of edges that are connected with that node. A node
with degree 0 is called as isolated node.
Adjacency Matrix
The Adjacency Matrix is a V×V matrix where the values are filled with either 0 or 1. If the link exists between Vi and Vj,
it is recorded 1; otherwise, 0.
The adjacency matrix –
In computer programming, a matrix can be defined with a 2-dimensional array. Any array with 'm' columns
and 'n' rows represent a m X n matrix. There may be a situation in which a matrix contains more number of
ZERO values than NON-ZERO values. Such matrix is known as sparse matrix.
Array Representation
In this representation, we consider only non-zero values along with their row and column index values. In
this representation, the 0th row stores the total number of rows, total number of columns and the total number
of non-zerovalues in the sparse matrix.
For example, consider a matrix of size 5 X 6 containing 6 number of non-zero values. This matrix can be
represented as
In above example matrix, there are only 6 non-zero elements ( those are 9, 8, 4, 2, 5 & 2) and matrix size is
5 X 6. We represent this matrix as shown in the above image. Here the first row in the right side table is
filled with values 5, 6 & 6 which indicates that it is a sparse matrix with 5 rows, 6 columns & 6 non-zero
values. The second row is filled with 0, 4, & 9 which indicates the non-zero value 9 is at the 0th-row 4th
column in the Sparse matrix. In the same way, the remaining non-zero values also follow a similar pattern.
A minimum spanning tree can be defined as the spanning tree in which the sum of the
weights of the edge is minimum. The weight of the spanning tree is the sum of the weights
given to the edges of the spanning tree. In the real world, this weight can be considered as
the distance, traffic load or any random value.
1.Prim's Algorithm
2.Kruskal's Algorithm
1.Prim's algorithm - It is a greedy algorithm that starts with an empty spanning tree. It is
used to find the minimum spanning tree from the graph. This algorithm finds the subset of
Lvr Degree College
edges that includes every vertex of the graph such that the sum of the weights of the edges
can be minimized.
Example:
The working of prim's algorithm using an example. It will be easier to understand the
prim's algorithm using an example.
Step 1 - First, we have to choose a vertex from the above graph. Let's choose B.
Step 2 - Now, we have to choose and add the shortest edge from vertex B. There are two
edges from vertex B that are B to C with weight 10 and edge B to D with weight 4. Among the
edges, the edge BD has the minimum weight. So, add it to the MST.
Step 3 - Now, again, choose the edge with the minimum weight among all the other edges. In
this case, the edges DE and CD are such edges. Add them to MST and explore the adjacent of
C, i.e., E and A. So, select the edge DE and add it to the MST.
Step 5 - Now, choose the edge CA. Here, we cannot select the edge CE as it would create a
cycle to the graph. So, choose the edge CA and add it to the MST.
So, the graph produced in step 5 is the minimum spanning tree of the given graph.
The cost of the MST is given below –
Cost of MST = 4 + 2 + 1 + 3 = 10 units.
2.Kruskal's algorithm - This algorithm is also used to find the minimum spanning tree
for a connected weighted graph. Kruskal's algorithm also follows greedy approach,
which finds an optimum solution at every stage instead of focusing on a global
optimum.
Example:
Edge AB AC AD AE BC CD DE
Weight 1 7 10 5 3 4 2
Now, sort the edges given above in the ascending order of their weights.
Edge AB DE BC CD AE AC AD
Weight 1 2 3 4 5 7 10
Step 2 - Add the edge DE with weight 2 to the MST as it is not creating the cycle.
Step 3 - Add the edge BC with weight 3 to the MST, as it is not creating any cycle or
loop.
Step 5 - After that, pick the edge AE with weight 5. Including this edge will create the
cycle, so discard it.
Step 6 - Pick the edge AC with weight 7. Including this edge will create the cycle, so
discard it.
Step 7 - Pick the edge AD with weight 10. Including this edge will also create the
cycle, so discard it.
So, the final minimum spanning tree obtained from the given weighted graph by using
Kruskal's algorithm is -
Google maps uses graphs for building transportation systems, where intersection of two(or more) roads
are considered to be a vertex and the road connecting two vertices is considered to be an edge, thus their
navigation system is based on the algorithm to calculate the shortest path between two vertices.
In World Wide Web, web pages are considered to be the vertices. There is an edge from a page u to other
page v if there is a link of page v on page u. This is an example of Directed graph. It was the basic idea
behind Google Page Ranking Algorithm.
Facebook uses graphs. Using graphs suggests mutual friends. it shows a list of the f following pages,
friends, and contact list.
On social media sites, we use graphs to track the data of the users. liked showing preferred post
suggestions, recommendations, etc.
Graphs are used in biochemical applications such as structuring of protein, DNA etc.
textbooks reffered :
websites reffered:
1.https://www.geeksforgeeks.org
2.https://www.javatpoint.com
3.https://github.com