Data Structures Using C Material For Ambedkar University Degree Students
Data Structures Using C Material For Ambedkar University Degree Students
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’.
Introduction to the Theory of Data Structures:
The study of computer science includes the study of organization and flow of data in a computer.
Data structures is the branch of computer science that releases the knowledge of
how the data should be organized?
how the flow of data should be controlled? and
how the data structure should be designed and implemented to reduce the
complexity and increases the efficiency of the algorithm?
Theory of data structures also helps you to understand and use the concept of abstraction,
analyze problems step by step and develop algorithms to solve real world problems. It enables
you to design and implement various data structures like stack, queues, linked lists, trees and
graphs.
Effective use of principals of data structures increases efficiency of algorithms to solve problems
like searching, sorting.
Need of data structures:
A data structure helps you to understand the relationship of one data element with
the other and organize it within the memory.
Sometimes the organization might be simple and can be very clearly vision.
A data structure helps you to analyze the data, store it and organize it in a
logical or mathematical manner.
As applications are getting complexed and amount of data is increasing day by
day, there may arise the following problems:
Processor speed: To handle very large amount of data, high speed processing is required,
but as the data is growing day by day to the billions of files per entity, processor may fail to
deal with that much amount of data.
Data Search: Consider an inventory size of 106 items in a store, If our application needs to
search for a particular item, it needs to traverse 106 items every time, results in slowing down
the search process.
Multiple requests: If thousands of users are searching the data simultaneously on a web
server, then there are the chances that a very large server can be failed during that process
Integer Representation:
An integer is the basic data type which is commonly used for storing negative as well as non-
negative integer numbers. The non-negative data is represented using binary number system.
In this, each bit position represents the power of 2. The right most bit position represents 20
which is 1.
For negative binary numbers the methods of representation used
are one’s complement and two’s complement.
Ex:
00100110
1’s complement is 1 1 0 1 1 0 0 1
11011001
11011010
on:
Real Number Representati
The method used to represent real numbers in computers is floating – point notation.
In this notation, the real number is represented by a number called a mantissa, times a base
raised to an integer power, called an exponent.
For Example, 209.52 could be represented as 20952 x 10-2. The mantissa is 20952 and
exponent is -2. Both mantissa and exponents are 2’s complementary binary integers.
Now the above example can be representation as
20952 is 00000000101000111011 (it is in 24-bit representation)
-2 is 11111110 (it is in 8-bit representation)
Computers work in binary. As a result, all characters, whether they are letters, punctuation or digits,are
stored as binary numbers. All of the characters that a computer can use are called a character set.
ASCII
ASCII uses seven bits, giving a character set of 128 characters. The characters are represented in atable
called the ASCII table. The 128 characters include:
Extended ASCII
Extended ASCII uses eight bits, giving a character set of 256 characters. This allows for specialcharacters
such as those with accents in languages such as French and Spanish.
Unicode
While suitable for representing English characters, 256 characters is far too small to hold every character in
other languages, such as Chinese or Arabic. Unicode uses 16 bits, giving a range of 65,536 characters. It
more suitable for languages with a large number of characters, but it requires more memory.
Abstract Data type (ADT) is a type (or class) for objects whose behavior is defined by a set of
value and a set of operations.
The definition of ADT only mentions what
operations are to be performed but not how these operations
will be implemented. It does not specify how data will be
organized in memory and what algorithms will be used for
implementing the operations. It is called “abstract” because it
gives an implementation-independent view. The process of
providing only the essentials and hiding the details is
known as abstraction.
The user of data type does not need to know how that data type is implemented, for example, we
have been using Primitive values like int, float, char data types only with the knowledge that these data type
can operate and be performed on without any idea of how they are implemented.
So a user only needs to know what a data type can do, but not how it will be implemented.
Stack ADT allows all data operations at one end only. At any given time, we can only access the top
element of a stack.
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.
Data types
Each programming language has its own set of data types. There are several definitions like
The term data type refers to the implementation of the mathematical model specified by an ADT.
Once such abstract data type is defined and the legal operations involving that type or specified it can
then be implemented.
Different types of data required different interpretation of bit strings while reading or
writing.
Primitive data types are basic data types of any language that form the basic unit for the data structure
Defined by the user.
A primitive data type defines how the data will be internally represented in, stored, and retrieved from the
memory.
Few primitive data types which are commonly available with most programming languages are
Integer
character
Integer:
An integer data type is a primitive data type that may represent a range of numbers from -2(n-1) +1 to
2(n-1) +1 where n depends upon the number of bits used to constitutes one word in the computer.
Character:
At a more general level, information can be represented in the form of characters. Any symbol from
set 0 – 9, A – Z, a – z and other special symbols is a character. Most of the computers use 8 bits to represent
a character. So that it has 256 characters of 8 bits. The number of bits necessary to represent a character in a
particular computer is called the byte size.
Real number consists of two parts mantissa and characteristic. A real number data type is generally
denoted with the term float. This is because computers usually represent a real number using a floating
point notation. There are many varieties of float floating point mutations and each has individual
characteristic.
Where m is the mantissa and n is the base (which is fixed 10) and r is the exponent.
For example in a tree Data Structure each node is related to each other in a
parent child relationship.
coefficient
exponent
The two components together form a composite type structure to represent a polynomial.
Atomic type:
Generally, Data Structure is represented by a memory block which has two parts.
Data storage
address storage
This facilities in storing the data and relating it to some other data by means of storing pointers in the
address part.
An atomic type data is a data structure that consists only the data items and not the pointers does for a
list of data items several atomic type nodes may exist each with a single data item corresponding to one of
the legal data types.
In the above list of atomic nodes maintained using list of notes in each node type represents the type
of data stored in the atomic note to which the list node points. 1 stands integer type, 2 for real number and 3
for character type or any different assumption can be made at implementation level to indicate different
data types
Difference between abstract data types, data types and data structures:
To avoid confusion between abstract data types data types and data structures it is relevant to
understand the relationship between the tree
An abstract type is the specification of the data type which specifies the logical and
mathematical model of the data type.
Data Type is the implementation of the extract data type.
Data structures refers to the collection of computer variables that are connected in some
specific manner .
Thus, there seems to be an open relationship between the tree that is a data type has its root in the
Abstract data type and the data structure comprises a set of computer variables of same or different data
types.
Refinement stages
The best approach to solve a complex problem is to divide it into smaller parts such that each part
becomes an independent module which is easy to manage.
This helps in understanding the problem analyzing solution and handling this problem efficiently.
The application or the nature of problem determines the number of refinement stages required in the
specification process. Different problems have different number of requirement stages but in general there
are four levels of refinement processes.
At this level we decide how the data is related to each other and what operations are needed details
about how to store data and how various operations are performed on the data or not decide at this level.
Data Structure level we decide about the operations on the data as needed by our problem for
example we decide what kind of data structure will be required to solve the problem.
At implementation level we decide the details of how the data structure will be represented in the
Computer memory.
Application level :
This level settles all details required for a particular application such as names for variables are
special requirements for the operations imposed by applications.
I
In the above figure, the first two levels are often called conceptual. The middle two levels can be
called an algorithmic yes they are concerned with representing data and the operations performed on the
same last level is basically concerned with the programming.
Software engineering
Software engineering is the theory and practice of methods helpful for the construction and
maintenance of large software systems. Development of good software is a tedious process which
continuous for long time before the software or program gets the final score of India is put into use. There
are many stages in the software development cycle. The process is often referred to as software
development life cycle (SDLC).
Program design
Program design can be considered as an important phase of the software development life
cycle. It is in this space that the algorithms and data structures to solve a problem or proposed. Some
important points are
As the design stage involves taking the specification and designing solutions to the problem.
Another important point which should be kept in mind while developing a solution strategy is
that it should work correctly in all conditions.
Generally the people who use the system or not aware of the program design you have adopted.
Thus, carries a system manual which is a detailed guide to how the design was achieved.
Large program should be divided into small modulus and the submodules by following one of
the two decomposition approaches.
Top down approach
Bottom up approach
Other important criteria by which a program can be judged for execution time and storage requirement.
Algorithms
The term algorithm refers to the sequence of instructions that must be followed to solve a
problem. In other words an algorithm is a logical representation of the instructions which should be
executed to perform a meaningful task.
After an algorithm has been designed its efficiency must be analyzed. This involves
determining whether the algorithm is economical in the use of computer resources. The importance of
efficiency of an algorithm is in the correctness that is does it always produce the correct result and program
complexity which is considered as both the difficulty of implementing an algorithm along with its
efficiency.
System may be divided into smaller units called modulus. Advantage of modularity is that it
always the principle of separation of concerns to be applied into two phases: When dealing with details of
each module in isolation and when dealing with overall characteristics of all modules and their
relationships in order to integrate them into a system: Modularity enhances design clarity, which in turn
cases implementation debugging testing documenting and maintaining of the product.
A system consists of components, which have components of their own. Indeed system is a
hierarchy of components. The highest level component corresponds to the total system. To design such a
hierarchy there are two possible approaches.
In top down approach which start from a abstract design. In each step design is defined into
most concentrate level until we reach the level where no more refinement is needed and the design can be
implemented directly.
Bottom up approach:
A bottom up design thought with designing the most basic or primitive components and the
proceeds to higher level components. Bottom up method works layers of abstraction. Starting from the very
bottom the operations that provide a layer of abstraction or implemented. The operations of this there are
then used to implement more powerful operations and still higher layer of abstraction. Until the stage is
reached with the operations supported by the layer are those designed by the system.
Large programs are divided into smaller programs Programs are divided into what are known as
which is known as decomposition. objects is called Composition.
Communication is less among the modules. Communication is a key among the modules.
The top-down approach is mainly used by Structured The bottom-up approach is used by Object-Oriented
programming languages like C, Fortran, etc. programming languages like C++, C#, Java, etc.
Complexity
Time complexity
space complexity
Time complexity:
According to the third algorithm the frequency count for the statement equals to a +
1 is n square as the inner loop runs and times each time the outer loop runs the outer loop also runs for n
times so n square is said to be different in increasing order of magnitude on n.
Space complexity:
The space needed by the program is the sum of the following components.
Fixed space requirement: This include the instruction space for simple variables fixed size structured
variables and constants.
Variable space requirement: This consists of space needed by a structured variable whose size depends
on particular instance of variables. It also includes the additional space required when the function uses
recursion.
Big "O" Notation:
If f(n) Represents the computing time of some algorithm and g(n) represents a known
standard function like n,n2,n log n, etc. then to write;
f(n) is O g(n)
means that f(n) of n is equal to biggest order of function g(n). This implies only when:
|f(n)|<=C|g(n)| for all sufficiently large integers n. Where she is the constant whose value depends upon
various factors.
Big O Notation helps to determine the time as well as space complexity of the algorithm.
Using the big O notation, the time taken by the algorithm space required to run the algorithm can be
ascertained. This information is useful to set the prerequisite is of algorithms and to develop and design
efficient algorithms in terms of time and space complexity.
The big O notation has been extremely useful to classify algorithms by their performance.
Developers use this notation to reach to the best solution for the given problem.
If the complexity of any algorithm is O(1) it means that the computing time of the
algorithm is constant O(n) and it is called linear time which implies that it is directly proportional to n.
O(n2) is called as quadratic time, O(n3) is the cubic time , O(2n) is exponential time, O(log n) and O(n log
n) are the logarithmic times.
O(1)
O(log n)
O(n)
O(n log n)
O(n2)
O(n3)
O(2n)
Algorithm analysis
Different ways of solving a problem and there are different algorithms which can be
designed to solve a problem. Therefore there is a difference between a problem and an algorithm. A
problem has a single problem statement that describes it in some general terms. However there are many
different ways to solve the problem and some of the solutions may be more efficient than the others.
Algorithm focuses on computation of space and time complexity which usually depends
on the size of the algorithm and input.
There are different types of time complexities which can be analyzed for the algorithm.
The best case time complexity of an algorithm is a measure of the minimum time
that the algorithm will required for an input of size n. The running time of mini algorithms where is not
only for the inputs of different sizes but also for the different inputs of same size.
The time that an algorithm will be required to execute a typical input data of size n
is known as average case time complexity. We can say that the value that is obtained by averaging the
running time of an algorithm for all possible inputs of size n can be determined average case time
complexity.
The term structured programming was coined by Dijkstra in the article structure
programming. We deal with various tools required for
building a structured program.
Recursion
A recursive routine is one whose design includes a call to itself. In design phase of
software development we use various problem solving methods in which recursion can be one of the
powerful tools.
Example:
Factorial(a)
Int a;
Int fact=1
If(a>1)
Fact=a*factorial(a-1)
Return(fact)
In the above example factorial function is calculating the values from the given number to
1. Each time it can be multiplied the number “a” with its previous number. This process will be continue till
the number reaches to 1. So, the same procedure can be done in several times. In that way the factorial is
used in recursive method to calculate the factorial of a given number.
Loops are used when we want to execute a part of the program or block of statement
several times there are many statements for the loop in purpose for example while, do while, for, switch
etc..
Recursive function is the function which calls itself again and again.
Principles of recursion
Some basic principles which are used while designing algorithm with recursion are
When beginning with the design of algorithm true recursion one should try to find out the key
step for the solution.
The stopping rule indicates that the problem or a substantial part of it is done and the execution
of the algorithm can be stopped.
After determining the key steps for the problem and finding the cases which are to be handled
the next step is to come by this to using an if statement to select between them.
Check termination
There should be taken to ensure that there will always terminate after the finite number of steps
and the stopping rule should also be satisfied.
Example:
Main()
If(n=0)
Else
While(n>1)
Fact*=n;
N--;
Factorial(a)
Int a;
Int fact=1
If(a>1)
Fact=a*factorial(a-1)
Return(fact)
A recursion tree can be defined as a part of tree showing the recursive calls. Therefore if a
procedure or function makes only one recursive call to itself, it's a recursion tree is simple, rather it is a
chain, each vertex having only one child.
Example:
In the problem of finding the factorial of a number, the main task is to calculate the factorial
from (n-1) down to 1. Recursion tree for calculating factorial from bottom to top we can obtain an iterator
program. Thus, when the tree is reduced to chain, the transformation of recursion to iteration is easy and
saves both space and time.
Thus, in this example of finding factorial, after studying the recursion tree it was found that using iteration
is easy and economical than recursion.
But on the other hand, the recursion tree for calculating Fibonacci series is not a chain but
contains many vertices signifying duplicate tasks. When a recursive program is Run, it sets up a stack to
use while traveling the tree, but if the result stored on the stacks are discarded rather than kept in some
other data structure for further use, then a great deal of duplication of work may occur as in recursive
calculation of Fibonacci series.
Therefore, for the Fibonacci numbers we need additional temporary variables to hold the information
required for calculating the current number.
Finally, by setting up for taking another data structure for such type of calculation, it is
possible to change any recursive program into the non recursive form.
Comments
Preprocessor directives
Global variables
Main()
Local variables
Statements
----------------------
-------------------------
Func1()
Local variables
Statements
----------------------
-------------------------
Func2()
Local variables
Statements
----------------------
-------------------------
C program start with comments between /* and */. Comments can be given anywhere
in the program.
The preprocessor directives are executed before C program code passes through the
compiler. These preprocessor directives make programs more efficient. Most commonly used directives are
#include which includes files, # define which defines the Macro name and macro expansion.
Example:
#include<stdio.h>
#define true 1;
Decoration for global variables, which have same data type and same name throughout
the function and are defined outside the main() function. But the declaration of too many Global variables
is not advisable. To make the program more efficient constants are used. Constants are values that can be
stored in the memory and can be changed during the education of program. They can be defined for
numeric, character and string data.
Another word which can be used is typedef which is used for defining new data types.
Syntax:-
Here type is the data type and data name is the user defined and name.
C program contains the main() function but a program should be divided into
functions. A function is a self contained sub-program which performs some specific, well defined task.
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
Example:
1. Array: An array is a Linear Data structure. It is a collection of items stored at contiguous memory
locations. The idea is to store multiple items of the same type together. Array elements can be accessed
with the help of index.
2. Linked list A linked list is a linear data structure, which is connected together via links. Linked
List is a sequence of nodes which contains items. Each link contains a connection to another link.
Linked list is the second most-used data structure after array.
3. Stacks: stack is a linear data structure, collection of items of the same type. Stack follows the
Last In First Out (LIFO) fashion wherein the last element entered is the first one to be popped out. In
stacks, the insertion and deletion of elements happen only at one endpoint of it.
4. Queues : Queue is a linear data structure that follows the FIFO rule (First In First Out). Insertion is
done from the back (the rear end) and deletion is done from the front.
Graph: A graph is a non linear data structure basically uses two components vertices and edges.
In the graph, Edges are used to connect vertices.
Q: What are the Differences between Linear and Non Linear Data Structures?
Types of Arrays:
Arrays are classified into two types. They are as follows...
1. Single Dimensional Array / One Dimensional Array
2. Multi Dimensional Array
1. Single Dimensional Array:
Single dimensional arrays are used to store a row of values of same data type. In single
dimensional array, data is stored in linear form. Single dimensional arrays are also called as one-
dimensional arrays, Linear Arrays or simply 1-D Arrays.
Declaration of Single Dimensional Array:
We use the following general syntax for declaring a single dimensional array...
Syntax: datatype arrayName [ size ] ;
Example:- int marks[60] ;
The above declaration of single dimensional array reserves 60 continuous memory locations of 2
bytes each with the name 'marks' and tells the compiler to allow only integer values into those
memory locations.
return 0;
}
Advantages:
It is used to represent multiple data items of same type by using only single name.
It can be used to implement other data structures like linked lists, stacks, queues, trees,
graphs etc.
It allows to store the elements in any dimensional array - supports multidimensional array
like 2D arrays are used to represent matrices.
Iterating the arrays using their index is faster compared to any other methods like linked
list etc.
Arrays are well known in applications such as searching, sorting and matrix operations.
Disadvantages:
It allows us to enter only fixed number of elements into it. We cannot alter the size of the
array once array is declared. We must know in advance(compile time) that how many
elements are to be stored in array.
The elements of array are stored in consecutive memory locations. So insertions and
deletions are very difficult and time consuming.
Applications of Arrays:
In programming languages arrays are used in wide range of applications. Few of them are as
follows...
Arrays are used to Store List of values.
Arrays are used to Perform Matrix Operations like matrix addition, subtraction, multiplication,
transpose.
Arrays are used to implement Search Algorithms like linear search, binary search.
Arrays are used to implement Sorting Algorithms like bubble sort, selection sort, insertion
sort, quick sort.
Arrays are used to implement Data structures like stack, queue.
Arrays are also used to implement CPU Scheduling Algorithms.
Arrays are efficient for sparse matrix representation to save memory
Operations on Array Data Structures:
The basic operations that are performed on data structures are as follows:
Insertion: Insertion means addition of a new data element in a data structure.
Deletion: Deletion means removal of a data element from a data structure if it is found.
Searching: Searching involves searching for the specified data element in a data structure.
Traversal: Traversal of a data structure means processing all the data elements present in it.
Sorting: Arranging data elements of a data structure in a specified order is called
sorting
Merging: Combining elements of two similar data structures to form a new data structure of
the same type, is called merging.
Copying : Copying array elements to another array will yield an array of the same length
and elements as the original one.
}
}
}
void insert(int ele,int loc)
{
for(int i=n-1;i>=loc;i--)
{
arr[i+1]=arr[i];
}
arr[loc]=ele;
n=n+1;
}
Value of p variable is 50
Pointer to an array
We can also point the whole array using pointers.
Using the array pointer, we can easily manipulate the multi-dimensional array.
Example
int arr[5] = {10, 20, 30, 40, 50};
int *ptr[5];
ptr = &arr;
Where, ptr points the entire array.
Advantages of pointers:
Pointers are more efficient inhandling Arrays and Structures.
Pointers allow references to function and thereby helps in passing of function as arguments to
other functions.
It reduces length of the program and its execution time as well.
It allows C language to support Dynamic Memory management
Q: Explain about Linked list and its types?
Linked List
Linked List can be defined as collection of objects called nodes that are randomly stored
in the memory.Each element in a linked list is called as "Node".
In a single linked list, the address of the first node is always stored in a reference node known as
"head" , last node is known as "tail" node and its link field must be NULL.
Operations
In a single linked list we perform the following operations...
Insertion
Deletion
Display
Insertion
The insertion into a singly linked list can be performed at different positions. Based on the position of
the new node being inserted, the insertion is categorized into the following categories.
The node can be inserted in three ways
1. Insert at the beginning
2. Insert at the End
3. Insert at the Middle
1)Inserting a node at begin:
It involves inserting any element at the front of the list. The steps are as follows
Insert at the beginning
Allocate memory for new node
Store data
Change next of new node to point to head
Change head to point to recently created node
Insert at the End
Allocate memory for new node
Store data
Traverse to last node
Change next of last node to recently created node
Node Creation:
struct node
{
int data;
struct node *next;
};
Deletion and Traversing
The Deletion of a node from a singly linked list can be performed at different positions. Based on the
position of the node being deleted, the operation is categorized into the following categories.
The Deletion of the node can be done in 3 ways.
1)Deletion at beginning
2) Deletion at ending position
3) Deletion at middle postion
1)Deletion at beginning:
It involves deletion of a node from the beginning of the list. This is the simplest operation among all.
It just need a few adjustments in the node pointers
head = head->next;
3)Deletion at
specified Position:
Ask the node position which has to be deleted.
Start traversing the list from the head node and move up to that position.
While traversing, keep track of the previous node to the node to be deleted.
Once you reach the position make the previous node link next node link of position node
Program: Write a c program to implement linked list operations
#include <stdio.h>
#include <stdlib.h>
Struct node
{ int data;
struct node *next;
};
void create(int data);
void display();
struct node *head, *tail = NULL;
void main()
{ create(10);create(20);create(30);create(40);
display();
}
void create(int data)
{
struct node *newNode = (struct node*)malloc(sizeof(struct node));
newNode->data = data;
newNode->next = NULL;
if(head == NULL) {
head =tail= newNode;
}
else {
tail->next = newNode;
tail = newNode; }
}
void display() {
struct node *temp = head;
if(head == NULL) {
printf("List is empty\n");
return;
}
printf("Nodes of singly linked list: \n");
while(temp != NULL) {
printf("%d ",temp->data);
temp = temp->next;
}
printf("\n");
}
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) , pointer to the
previous node (previous pointer). A sample node in a doubly linked list is shown in the figure.
A doubly linked list containing three nodes having numbers from 1 to 3 in their data part, is shown in
the following image.
In C, structure of a node in doubly linked list can be given as :
struct node
{
struct node *prev;
int data;
struct node *next;
}
The prev part of the first node and the next part of the last node will always contain null indicating
end in each direction.
In a singly linked list, we could traverse only in one direction, because each node contains address of
the next node and it doesn't have any record of its previous nodes.
However, doubly linked list overcome this limitation of singly linked list. Due to the fact that, each
node of the list contains the address of its previous node, we can find all the details about the previous
node as well by using the previous address stored inside the previous part of each node.
Node Creation
struct node
{
struct node *prev;
int data;
struct node *next;
};
All the remaining operations regarding doubly linked list are described in the following table.
SN Operation Description
1 Inseinsertion at beginning Adding the node into the linked list at beginning.
2 Inseinsertion at end Adding the node into the linked list to the end.
5 Deletion at the end Removing the node from end of the list.
6Deletion of the node having given Removing the node which is present just after the node
data containing the given data.
7 Searching Comparing each node data with the item to be searched and
return the location of the item in the list if the item found else return
null.
#include<stdio.h>
void create();
void ftraverse();
void rtraverse();
struct dnode
int data;
};
struct dnode *first,*last;
void main()
{
create();
create();
create();
ftraverse();
rtraverse();
void create()
scanf("%d",&new->data);
new->prev=NULL;
new->next=NULL;
if(first==NULL)
{
first=last=new;
}
else
last->next=new;
new->prev=last;
last=new;
}}
void ftraverse()
{
while(temp!=NULL)
printf("%d\t",temp->data);
temp=temp->next;
void rtraverse()
while(temp!=NULL)
printf("%d\t",temp->data);
temp=temp->prev;
}
Circular Singly Linked List
Circular linked list is a linked list where all nodes are connected to form a circle. There is no
NULL at the end. A circular linked list can be a singly circular linked list or doubly circular
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 traverse a circular singly linked list until we reach the same node where we started.
The circular singly liked list has no beginning and no ending.
There is no null value present in the next part of any of the nodes.
The following image shows a circular singly linked list.
Example: circular linked list are being used in computer science including browser surfing where a
record of pages visited in the past by the user, is maintained in the form of circular linked lists and can
be accessed again on clicking the previous button.
1 Insertion at beginning Adding a node into circular singly linked list at the beginning.
2 Insertion at the end Adding a node into circular singly linked list at the end.
1 Deletion at beginning Removing the node from circular singly linked list at the
beginning.
2 Deletion at the end Re moving the node from circular singly linked list at the end.
3 Searching compare each element of the node with the given item and ret
urn the location at which the item is present in the list otherwise
r return null.
4 Traversing Vi visiting each element of the list at least once in order to perform
some specific operation.
Uses of Linked List
The list is not required to be contiguously present in the memory. The node can reside any
where in the memory and linked together to make a list. This achieves optimized
utilization of space.
list size is limited to the memory size and doesn't need to be declared in advance.
Empty node can not be present in the linked list.
We can store values of primitive types or objects in the singly linked list
In a stack, the insertion operation is performed using a function called "push" and deletion operation is
performed using a function called "pop". In the figure, PUSH and POP operations are performed at top
position in the stack. That means, both the insertion and deletion operations are
performed at one end i.e., at Top.
Example:
If we want to create a stack by inserting 10,45,12,16,35 and 50. Then 10
becomes the bottom most element and 50 is the top most element. Top is at 50 as
shown in the image below.
Stack ADT: An abstract data type is a data type defined in terms of a type and a set of operations on that
type. Each operation is defined in terms of its input and output without specifying how the data type is
implemented. A stack ADT, defined in terms of push and pop operations.
The Stack ADT Definition A stack is a restricted list in which entries are added and removed
from the same end, called the top. This strategy is known as last-in-first-out (LIFO) strategy. Operations
(methods) on stacks:
push (item): Insert element into stack
pop () : Removes an element from stack
Peek () : Returns the top element from the stack
Is Empty () : Returns true if the stack is empty
is Full() : Returns true if the stack is full
display : Used to display all the elements
A stack data structure can be implemented by using a linked list data structure. The
stack implemented using linked list can work for an unlimited number of values. That means, stack
implemented using linked list works for the variable size of data. So, there is no need to fix the size at the
beginning of the implementation. The Stack implemented using linked list can organize as many data values
as we want.
In linked list implementation of a stack, every new element is inserted as 'top' element.
That means every newly inserted element is pointed by 'top'. Whenever we want to remove an element from
the stack, simply remove the node which is pointed by 'top' by moving 'top' to its previous node in the list.
The next field of the first element must be always NULL.
Example
In the above example, the last inserted node is 99 and the first inserted node is 25. The order of elements
inserted is 25, 32,50 and 99.
void push(int);
void pop();
void display();
void main()
{
int choice, value;
clrscr();
printf("\n:: Stack using Linked List ::\n");
while(1){
printf("\n****** MENU ******\n");
printf("1. Push\n2. Pop\n3. Display\n4. Exit\n");
printf("Enter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d", &value);
push(value);
break;
case 2: pop(); break;
case 3: display(); break;
case 4: exit(0);
default: printf("\nWrong selection!!! Please try again!!!\n");
}
}
}
Postfix Expression:
In postfix expression, operator is used after operands. We can say that "Operator follows the Operands".
The general structure of Postfix expression is as follows...
Example: Operand1 Operand2 Operator
Prefix Expression
In prefix expression, operator is used before operands. We can say that "Operands follows the Operator".
The general structure of Prefix expression is as follows...
Example: Operator Operand1 Operand2
Expression Conversion
Any expression can be represented using three types of expressions (Infix, Postfix and Prefix). We can
also convert one type of expression to another type of expression like Infix to Postfix, Infix to Prefix, Postfix
to Prefix and vice versa.
To convert any Infix expression into Postfix or Prefix expression we can use the following procedure...
Algorithm
Step 1 : Scan the Infix Expression from left to right.
Step 2 : If the scanned character is an operand, append it with final Infix to Postfix string.
Step 3 : Else,
Step 3.1 : If the precedence order of the scanned(incoming) operator is greater than the precedence order of
the operator in the stack (or the stack is empty or the stack contains a ‘(‘ or ‘[‘ or ‘{‘), push it on stack.
Step 3.2 : Else, Pop all the operators from the stack which are greater than or equal to in precedence than that
of the scanned operator. After doing that Push the scanned operator to the stack. (If you encounter
parenthesis while popping then stop there and push the scanned operator in the stack.)
Step 4 : If the scanned character is an ‘(‘ or ‘[‘ or ‘{‘, push it to the stack.
Step 5 : If the scanned character is an ‘)’or ‘]’ or ‘}’, pop the stack and and output it until a ‘(‘ or ‘[‘ or ‘{‘
respectively is encountered, and discard both the parenthesis.
Step 6 : Repeat steps 2-6 until infix expression is scanned.
Step 7 : Print the output
Step 8 : Pop and output from the stack until it is not empty.
In a queue data structure, the insertion operation is performed using a function called "enQueue()" and
deletion operation is performed using a function called "deQueue()".
Example: Queue after inserting 25, 30, 51, 60 and 85.
Operations on a Queue:
The following operations are performed on a queue data structure...
enQueue(value) - To insert an element into the queue
deQueue() - To delete an element from the queue
display() - To display the elements of the queue
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.
Queue data structure can be implemented in two ways. They are as follows...
1. Using Array
2. Using Linked List
When a queue is implemented using array, that queue can organize only limited number of elements.
When a queue is implemented using linked list, that queue can organize unlimited number of elements.
1. Queue implementation by Using Array:
A queue data structure can be implemented using one dimensional array. But,
queue implemented using array can store only fixed number of data values. The implementation of queue
data structure using array is very simple, we define a one dimensional array of specific size and insert or
delete the values into that array by using FIFO (First In First Out) principle with the help of
variables 'front' and 'rear'.
Initially both 'front' and 'rear' are set to -1. Whenever, we want to insert a new
value into the queue, increment 'rear' value by one and then insert at that position. Whenever we want to
delete a value from the queue, then increment 'front' value by one and then display the value at 'front'
position as deleted element.
Before we implement actual operations, first follow the below steps to create an empty queue.
Step 1: Declare all the user defined functions which are used in queue implementation.
Step 2: Create a one dimensional array with above defined SIZE (int queue[SIZE])
Step 3: Define two integer variables 'front' and 'rear' and initialize both with '-1'(int front = -1,rear = -1)
Step 4: Then implement main method by displaying menu of operations list and make suitable function calls
to perform operation selected by the user on queue.
Ex: Program for implementing Queue data structure and perform operations like creation, insert,
delete, display using Array Datastructure
#include<stdio.h>
#include<conio.h>
#define SIZE 10
void enQueue(int);
void deQueue();
void display();
int queue[SIZE], front = -1, rear = -1;
void main()
{
int value, choice;
clrscr();
while(1){
printf("\n\n***** MENU *****\n");
printf("1. Insertion\n2. Deletion\n3. Display\n4. Exit");
printf("\nEnter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d",&value);
enQueue(value);
break;
case 2: deQueue();
break;
case 3: display();
break;
case 4: exit(0);
default: printf("\nWrong selection!!! Try again!!!");
}
}
}
void enQueue(int value){
if(rear == SIZE-1)
printf("\nQueue is Full!!! Insertion is not possible!!!");
else{
if(front == -1)
front = 0;
rear++;
queue[rear] = value;
printf("\nInsertion success!!!");
}
}
void deQueue(){
if(front == rear)
printf("\nQueue is Empty!!! Deletion is not possible!!!");
else{
printf("\nDeleted : %d", queue[front]);
front++;
if(front == rear)
front = rear = -1;
}
}
void display(){
if(rear == -1)
printf("\nQueue is Empty!!!");
else{
int i;
printf("\nQueue elements are:\n");
for(i=front; i<=rear; i++)
printf("%d\t",queue[i]);
}
}
In above example, the last inserted node is 50 and it is pointed by 'rear' and the first inserted node is 10 and
it is pointed by 'front'. The order of elements inserted is 10, 15, 22 and 50.
Operations
To implement queue using linked list, we need to set the following things before implementing actual
operations.
Step 1 - Include all the header files which are used in the program. And declare all the user defined
functions.
Step 2 - Define a 'Node' structure with two members data and next.
Step 3 - Define two Node pointers 'front' and 'rear' and set both to NULL.
Step 4 - Implement the main method by displaying Menu of list of operations and make suitable function
calls in the main method to perform user selected operation.
void insert(int);
void delete();
void display();
void main()
{
int choice, value;
clrscr();
printf("\n:: Queue Implementation using Linked List ::\n");
while(1){
printf("\n****** MENU ******\n");
printf("1. Insert\n2. Delete\n3. Display\n4. Exit\n");
printf("Enter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d", &value);
insert(value);
break;
case 2: delete(); break;
case 3: display(); break;
case 4: exit(0);
default: printf("\nWrong selection!!! Please try again!!!\n");
}
}
}
void insert(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode -> next = NULL;
if(front == NULL)
front = rear = newNode;
else{
rear -> next = newNode;
rear = newNode;
}
printf("\nInsertion is Success!!!\n");
}
void delete()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
front = front -> next;
printf("\nDeleted element: %d\n", temp->data);
free(temp);
}
}
void display()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
while(temp->next != NULL){
printf("%d--->",temp->data);
temp = temp -> next;
}
printf("%d--->NULL\n",temp->data);
}}
Now consider the following situation after deleting three elements from the queue...
It says that Queue is Full and we cannot insert the new element because 'rear' is still at last position. In the
above situation, even though we have empty positions in the queue we can not make use of them to insert the
new element. This is the major problem in a normal queue data structure. To overcome this problem we use a
circular queue data structure.
Double Ended Queue can be represented in TWO ways, those are as follows...
1. Input Restricted Double Ended Queue
2. Output Restricted Double Ended Queue
Input Restricted Double Ended Queue
In input restricted double-ended queue, the insertion operation is performed at only one end and deletion
operation is performed at both the ends.
}
void deleteAtFront()
{
if(front==NULL)
{
printf("\nDEQueue is empty");
}
else
{ temp=front;
x=front->data;
front=front->next;
front->prev=NULL;
printf("\nElement deleted at front =%d",x);
}
}
void deleteAtRear()
{
if(front==NULL)
{
printf("\nDEQueue is empty");
}
else
{
x=rear->data;
temp=rear;
rear=rear->prev;
rear->next=NULL;
free(temp);
printf("\nElement deleted at rear =%d",x);
}
}
void main()
{
clrscr();
insertAtRear();
insertAtRear();
display();
insertAtFront();
display();
deleteAtFront();
display();
deleteAtRear();
display();
getch();
}
Tree Terminology
In tree data structure, every individual element is called as Node. Node in a tree data structure,
stores the actual data of that particular element and link to next element in hierarchicalstructure
In a tree data structure, if we have N number of nodes then we can have a maximum of N-
1 number of links.
Example:
Terminology
Root: In a tree data structure, the first node is called as Root Node. Every tree must have root node.
In any tree, there must be only one root node. We never have multiple root nodes in a tree.
Leaf: In a tree data structure, the node which does not have a child is called as LEAF Node. In
simple words, a leaf is a node with no child. Leaf nodes are also called as External Nodes. Leaf node
is also called as 'Terminal' node.
Height:
Path:
1)Binary Tree
Binary Tree: A binary tree is a tree in which a parent node which consists of maximum 2 childrens.
So it supports 0-2 nodes only.
Full Binary Tree: A Binary tree is known as full binary tree in which each parent node except leaf
nodes compulsory consists of two childrens.
Complete Binary Tree: A binary tree is a complete binary tree in which all elements forms a
sequence.
Binary Search Tree: A tree is a binary search tree it must be a binary tree. In binary search tree the
left child value always less than parent value and right child value always greater than the parent
value.
Binary Tree Representation: A binary tree data structure is represented using two methods.
Those methods are as follows...
1)Array Representation
The simplest way to represent binary tree is array representation .we use a one dimensional rray
(1-D Array) to store set of values.
In which
The parent element of a binary tree is stored in first location of the array.
If the parent node position is “I” its left child is placed in (2i+1)position and its right child is (2i+2) position.
We use double linked list to represent a binary tree. In a double linked list, every
node consists of three fields. First field for storing left child address, second for storing actual data
and third for storing right child address
Displaying (or) visiting order of nodes in a binary tree is called as Binary Tree Traversal.
In In-Order traversal, the root node is visited between left child and right
child. In this traversal, the left child node is visited first, then the root node is visited and later we go
for visiting right child node. This in-order traversal is applicable for every root node of all subtrees
in the tree.. This is performed recursively for all nodes in the tree.
I-D-J-B-F-A-G-K-C-H
That means here we have visited in the order of A-B-D-I-J-F-C-G-K-H using Pre-Order Traversal.
In Post-Order traversal, the root node is visited after left child and right child. In this
traversal, left child node is visited first, then its right child and then its root node. This is recursively
performed until the right most node is visited.
Example:
Here we have visited in the order of I - J - D - F - B - K - G - H -
C - A using Post-Order Traversal.
I-J-D-F-B-K-G-H-C–A
In a binary tree, every node can have maximum of two children but there is no order of nodes
based on their values. In binary tree, the elements are arranged as they arrive to the tree, from top
to bottom and left to right.
In a binary search tree, all the nodes in left subtree of any node contains smaller values and all the
nodes in right subtree of that contains larger values as shown in following figure...
Example:
The following tree is a Binary Search Tree. In this tree, left subtree of every node contains nodes
with smaller values and right subtree of every node contains
larger values.
Every Binary Search Tree is a binary tree but all the Binary
Trees need not to be binary search trees.
Search
Insertion
Deletion
In a binary search tree, the search operation is performed with O(log n) time complexity. The
search operation is performed as follows...
Step 2: Compare, the search element with the value of root node in the tree.
Step 3: If both are matching, then display "Given node found!!!" and terminate the function
Step 4: If both are not matching, then check whether search element is smaller or larger than that
node value.
Step 5: If search element is smaller, then continue the search process in left subtree.
Step 6: If search element is larger, then continue the search process in right subtree.
Step 7: Repeat the same until we found exact element or we completed with a leaf node
Step 8: If we reach to the node with search value, then display "Element is found" and terminate the
function.
Step 9: If we reach to a leaf node and it is also not matching, then display "Element not found" and
terminate the function.
In a binary search tree, the insertion operation is performed with O(log n) time complexity. In
binary search tree, new node is always inserted as a leaf node. The insertion operation is performed
as follows...
Step 1: Create a newNode with given value and set its left and right to NULL.
Step 4: If the tree is Not Empty, then check whether value of newNode is smaller or larger than the
node (here it is root node).
Step 5: If newNode is smaller than or equal to the node, then move to its left child. If newNode
is larger than the node, then move to its right child.
Step 6: Repeat the above step until we reach to a leaf node (e.i., reach to NULL).
Step 7: After reaching a leaf node, then isert the newNode as left child if newNode is smaller or
equal to that leaf else insert it asright child.
In a binary search tree, the deletion operation is performed with O(log n) time complexity. Deleting
a node from Binary search tree has follwing three cases...
Step 2: Delete the node using free function (If it is a leaf) and terminate the function.
We use the following steps to delete a node with one child from BST...
Step 2: If it has only one child, then create a link between its parent and child nodes.
Step 3: Delete the node using free function and terminate the function.
We use the following steps to delete a node with two children from BST...
Step 2: If it has two children, then find the largest node in its left subtree (OR) the smallest node in
its right subtree.
Step 3: Swap both deleting node and node which found in above step.
Step 4: Then, check whether deleting node came to case 1 or case 2 else goto steps 2
Step 7: Repeat the same process until node is deleted from the tree.
Example
10,12,5,4,20,8,7,15 and 13
Above elements are inserted into a Binary Search Tree as follows...
C program for implementing “Binary Search Tree”.
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct tnode
{
struct tnode *left;
int data;
struct tnode *right;
};
}
void main()
{
clrscr();
root=NULL;
create();
printf("\ninorder traversing =");
inorder(root);
printf("\npreorder traversing =");
preorder(root);
printf("\npostorder traversing =");
postorder(root);
getch();
}
UNIT-V
Introduction to Graphs
Graph is a collection of vertices and arcs which connects vertices in the graph
Graph is a collection of nodes and edges which connects nodes in the graph
Example
Graph Terminology
Vertex: A individual data element of a graph is called as Vertex. Vertex is also known as node. In
above example graph, A, B, C, D & E are known as vertices.
Edge: An edge is a connecting link between two vertices. Edge is also known as Arc. An edge is
represented as (starting Vertex , ending Vertex).
For example, in above graph, the link between vertices A and B is represented as (A,B). In above
example graph, there are 7 edges (i.e., (A,B), (A,C), (A,D), (B,D), (B,E), (C,D), (D,E)).
Undirected Edge - An undirected edge is a bidirectional edge. If there is a undirected edge between
vertices A and B then edge (A , B) is equal to edge (B , A).
Directed Edge - A directed edge is a unidirectional edge. If there is a directed edge between
vertices A and B then edge (A , B) is not equal to edge (B , A).
Undirected Graph: A graph with only undirected edges is said to be undirected graph.
Directed Graph: A graph with only directed edges is said to be directed graph.
Mixed Graph: A graph with undirected and directed edges is said to be mixed graph.
End vertices or Endpoints:The two vertices joined by an edge are called the end vertices (or
endpoints) of the edge.
Destination: If an edge is directed, its first endpoint is said to be origin of it and the other endpoint
is said to be the destination of the edge.
Adjacent: If there is an edge between vertices A and B then both A and B are said to be adjacent.
Outgoing Edge: A directed edge is said to be outgoing edge on its orign vertex.
Incoming Edge: A directed edge is said to be incoming edge on its destination vertex.
Parallel edges or Multiple edges: If there are two undirected edges to have the same end vertices,
and for two directed edges to have the same origin and the same destination. Such edges are called
parallel edges or multiple edges.
Simple Graph:A graph is said to be simple if there are no parallel and self-loop edges.
Path: A path is a sequence of alternating vertices and edges that starts at a vertex and ends at a
vertex such that each edge is incident to its predecessor and successor vertex.
Adjacency Matrix
Incidence Matrix
Adjacency List
Adjacency Matrix
In this matrix, rows and columns both represents vertices. This matrix is filled
with either 1 or 0. Here, 1 represents there is an edge from row vertex to column vertex and 0
represents there is no edge from row vertex to column vertex.
For example, consider the following undirected graph representation...
Incidence Matrix
In this matrix, rows represents vertices and columns represents edges. This
matrix is filled with either 0 or 1 or -1. Here, 0 represents row edge is not connected to column
vertex, 1 represents row edge is connected as outgoing edge to column vertex and -1 represents
row edge is connected as incoming edge to column vertex.
For example, consider the following directed graph representation...
Adjacency List
In this representation, every vertex of graph contains list of its adjacent vertices.
For example, consider the following directed graph representation implemented using linked list...
Graph traversal is technique used for searching a vertex in a graph. The graph
traversal is also used to decide the order of vertices to be visit in the search process. A graph
traversal finds the egdes to be used in the search process without creating loops that means using
graph traversal we visit all verticces of graph without getting into looping path.
There are two graph traversal techniques and they are as follows...
DFS (Depth First Search): DFS traversal of a graph, produces a spanning tree as final
result. Spanning Tree is a graph without any loops. We use Stack data structure with maximum size
of total number of vertices in the graph to implement DFS traversal of a graph.
We use the following steps to implement DFS traversal...
Step 2: Select any vertex as starting point for traversal. Visit that vertex and push it on to the Stack.
Step 3: Visit any one of the adjacent vertex of the vertex which is at top of the stack which is not
visited and push it on to the stack.
Step 4: Repeat step 3 until there are no new vertex to be visit from the vertex on top of the stack.
Step 5: When there is no new vertex to be visit then use back tracking and pop one vertex from the
stack.
Step 7: When stack becomes Empty, then produce final spanning tree by removing unused edges
from the graph
Back tracking is coming back to the vertex from which we came to current vertex.
Example:
Graph Traversals - BFS
Step 2: Select any vertex as starting point for traversal. Visit that vertex and insert it into the
Queue.
Step 3: Visit all the adjacent vertices of the vertex which is at front of the Queue which is not
visited and insert them into the Queue.
Step 4: When there is no new vertex to be visit from the vertex at front of the Queue then delete
that vertex from the Queue.
Step 6: When queue becomes Empty, then produce final spanning tree by removing unused edges
from the graph
Example
Q:What is a Minimum Spanning Tree?
The cost of the spanning tree is the sum of the weights of all the edges in the
tree. There can be many spanning trees. Minimum spanning tree is the spanning tree where the
cost is minimum among all the spanning trees. There also can be many minimum spanning trees.
There are two famous algorithms for finding the Minimum Spanning Tree:
Kruskal’s Algorithm
Kruskal’s Algorithm builds the spanning tree by adding edges one by one into
a growing spanning tree. Kruskal's algorithm follows greedy approach as in each iteration it finds
an edge which has least weight and add it to the growing spanning tree.
Algorithm Steps:
Start adding edges to the MST from the edge with the smallest weight until the edge of the
largest weight.
Only add edges which doesn't form a cycle , edges which connect only disconnected
components.
Prim’s Algorithm
Prim’s Algorithm also use Greedy approach to find the minimum spanning tree. In
Prim’s Algorithm we grow the spanning tree from a starting position. Unlike an edge in Kruskal's,
we add vertex to the growing spanning tree in Prim's.
Algorithm Steps:
Maintain two disjoint sets of vertices. One containing vertices that are in the growing
spanning tree and other that are not in the growing spanning tree.
Select the cheapest vertex that is connected to the growing spanning tree and is not in the
growing spanning tree and add it into the growing spanning tree. This can be done using
Priority Queues. Insert the vertices, that are connected to growing spanning tree, into the
Priority Queue.
Check for cycles. To do that, mark the nodes which have been already selected and insert
only those nodes in the Priority Queue that are not marked.
Algorithm
Create a set sptSet (shortest path tree set) that keeps track of vertices included in the shortest-
path tree, i.e., whose minimum distance from the source is calculated and finalized. Initially,
this set is empty.
Assign a distance value to all vertices in the input graph. Initialize all distance values as
INFINITE. Assign distance value as 0 for the source vertex so that it is picked first.
While sptSet doesn’t include all vertices
a) Pick a vertex u which is not there in sptSet and has a minimum distance value.
b) Include u to sptSet.
….c) Update distance value of all adjacent vertices of u. To update the distance values, iterate
through all adjacent vertices. For every adjacent vertex v, if the sum of distance value of u
(from source) and weight of edge u-v, is less than the distance value of v, then update the
distance value of v.
Now pick the vertex with a minimum distance value. The vertex 0 is picked, include it in sptSet. So
sptSet becomes {0}. After including 0 to sptSet, update distance values of its adjacent vertices.
Adjacent vertices of 0 are 1 and 7. The distance values of 1 and 7 are updated as 4 and 8
. The following subgraph shows vertices and their distance values, only the vertices with finite
distance values are shown. The vertices included in SPT are shown in green colour.
Pick the vertex with minimum distance value and not already included in SPT (not in sptSET). The
vertex 1 is picked and added to sptSet. So sptSet now becomes {0, 1}. Update the distance values of
adjacent vertices of 1. The distance value of vertex 2 becomes 12.
Pick the vertex with minimum distance value and not already included in SPT (not in sptSET).
Vertex 7 is picked. So sptSet now becomes {0, 1, 7}. Update the distance values of adjacent vertices
of 7. The distance value of vertex 6 and 8 becomes finite (15 and 9 respectively).
Pick the vertex with minimum distance value and not already included in SPT (not in sptSET).
Vertex 6 is picked. So sptSet now becomes {0, 1, 7, 6}. Update the distance values of adjacent
vertices of 6. The distance value of vertex 5 and 8 are updated.
We repeat the above steps until sptSet includes all vertices of the given graph. Finally, we get the
following Shortest Path Tree (SPT).
Application of Graphs:
In computer science graph theory is used for the study of algorithms like:
Dijkstra's Algorithm
Prims's Algorithm
Kruskal's Algorithm
Physics and Chemistry: Graph theory is also used to study molecules in chemistry and
physics.
Social Science: Graph theory is also widely used in sociology.
Mathematics: In this, graphs are useful in geometry and certain parts of topology such as
knot theory.
Biology: Graph theory is useful in biology and conservation efforts.
Graph theory is used to find shortest path in road or a network.
In Google Maps, various locations are represented as vertices or nodes and the roads are
represented as edges and graph theory is used to find the shortest path between two nodes.
Sortings and searchings
Sorting:
Sorting is a technique to rearrange the elements of a list in ascending or descending order, which can be
numerical, lexicographical, or any user-defined order.
2. External Sorting
Internal Sorting: This method uses only the primary memory during sorting process. All data
items are held in main memory and no secondary memory is required this sorting process. If all the data
that is to be sorted can be accommodated at a time in memory is called internal sorting. There is a
limitation for internal sorts; they can only process relatively small lists due to memory constraints.
(i) Selection sort :- ex:- selection sort algorithm, heap sort algorithm
(ii) Insertion sort :- ex:- insertion sort algorithm, shell sort algorithm
(iii) Exchange sort :- ex:- Bubble Sort Algorithm, Quick sort algorithm
External Sorts:- Sorting large amount of data requires external or secondary memory. This process uses
external memory such as HDD, to store the data which is not fit into the main memory. So, primary
memory holds the currently being sorted data only. All external sorts are based on process of merging.
Different parts of data are sorted separately and merged together.
Bubble sort:
Bubble sort is a simple sorting technique. In bubble sort method the list is divided into two sub-lists
sorted and unsorted. This sorting follows comparison-based algorithm in which each pair of adjacent
elements are compared and the elements are swapped if they are not in order. . The bubble sort was
originally written to bubble up the highest element in the list. Given a list of ‘n’ elements the bubble sort
requires up to n-1 passes to sort the data.
Algorithm :
Bubble_Sort ( A [ ] , N )
Step 3 : If ( A [ J] < A [ J – 1 ] )]
Swap ( A [ J ] , A [ J – 1])
End For
End For
Step 4 : Exit
Program for implementing bubble sort:
#include<stdio.h>
void main ()
int i, j,temp,n,a[20];
printf("enter no of elements");
scanf("%d",&n);
printf("enter elements");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
temp = a[i];
a[i] = a[j];
a[j] = temp;
printf("%d\n",a[i]);
}
Time Complexity of Bubble Sort :
The complexity of sorting algorithm is depends upon the number of comparisons that are made. Total
comparisons in Bubble sort is: n ( n – 1) / 2 ≈ n 2 – n
Insertion sort:
Both the selection and bubble sorts exchange elements. But insertion sort does not exchange
elements. Insertion sort algorithm arranges a list of elements in a particular order. In insertion sort the
element is inserted at an appropriate place similar to pack cards insertion. Here the list is divided into
two parts sorted and unsorted sub-lists. In each pass, the first element of unsorted sub list is picked up
and moved into the sorted sub list by inserting it in suitable position until all the elements are sorted in
the list.. Suppose we have ‘n’ elements, we need n-1 passes to sort the elements.
Step 1: Asume that first element in the list is in sorted portion of the list and remaining all elements are
in unsorted portion.
Step 2: Consider first element from the unsorted list and insert that element into the sorted list in order
specified.
Step 3: Repeat the above process until all the elements from the unsorted list are moved into the sorted
list.
#include<stdio.h>
void main ()
printf("enter no of elements");
scanf("%d",&n);
printf("enter elements");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
{
temp = a[k];
j= k-1;
a[j+1] = a[j];
j = j-1;
a[j+1] = temp;
for(i=0;i<n;i++)
printf("\n%d\n",a[i]);
Time complexity :
Worst case:o(n2)
Best case:o(n)
Merge sort:
Merge sort is a divide-and-conquer algorithm based on the idea of breaking down a list into
several sub-lists until each sublist consists of a single element and merging those sublists in a manner
that results into a sorted list.
#include<stdio.h>
void mergesort(int a[],int i,int j);
void merge(int a[],int i1,int j1,int i2,int j2);
int main()
{
int a[30],n,i;
printf("Enter no of elements:");
scanf("%d",&n);
printf("Enter array elements:");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
mergesort(a,0,n-1);
printf("\nSorted array is :");
for(i=0;i<n;i++)
printf("%d ",a[i]);
return 0;
}
void mergesort(int a[],int i,int j)
{
int mid;
if(i<j)
{
mid=(i+j)/2;
mergesort(a,i,mid); //left recursion
mergesort(a,mid+1,j); //right recursion
merge(a,i,mid,mid+1,j); //merging of two sorted sub-arrays
}
}
void merge(int a[],int i1,int j1,int i2,int j2)
{
int temp[50]; //array used for merging
int i,j,k;
i=i1; //beginning of the first list
j=i2; //beginning of the second list
k=0;
while(i<=j1 && j<=j2) //while elements in both lists
{
if(a[i]<a[j])
temp[k++]=a[i++];
else
temp[k++]=a[j++];
}
while(i<=j1) //copy remaining elements of the first list
temp[k++]=a[i++];
while(j<=j2) //copy remaining elements of the second list
temp[k++]=a[j++];
//Transfer elements from temp[] back to a[]
for(i=i1,j=0;i<=j2;i++,j++)
a[i]=temp[j];
}
Time Complexity :
Best case:o(nlog(n))
Linear search executes in O(n) time where n is the number of elements in the array.
Obviously, the best case of linear search is when VAL is equal to the first element of the array. In this
case, only one comparison will be made.
Likewise, the worst case will happen when either VAL is not present in the array or it is equal to the last
element of the array. In both the cases, n comparisons will have to be made.
Time Complexity :
Best case:o(n2)
Worst case: :o(n2)
#include <conio.h>
#include<stdio.h>
int main()
{
int a[50],i,n,key;
printf("Enter size of the array : ");
scanf("%d", &n);
printf("Enter elements in array : ");
for(i=0; i<n; i++)
{
scanf("%d",&a[i]);
}
printf("Enter the key : ");
scanf("%d", &key);
for(i=0; i<n; i++)
{
if(a[i]==key)
{
printf("element found at %d position ",i);
return 0;
}
Binary Search:
The binary search algorithm can be used with only sorted list of element. That
means, binary search can be used only with list of element which are already arranged in a order. The
binary search cannot be used for list of element which are not in order.
This search process starts comparing of the search element with the middle element
in the list. If both are matched, then the result is "element found". Otherwise, we check whether the
search element is smaller or larger than the middle element in the list. If the search element is smaller,
then we repeat the same process for left sub list of the middle element.
If the search element is larger, then we repeat the same process for right sub list of
the middle element. We repeat this process until we find the search element in the list or until we left
with a sub list of only one element. And if that element also doesn't match with the search element, then
the result is "Element not found in the list".
Step 3: Compare, the search element with the middle element in the sorted list.
Step 4: If both are matching, then display "Given element found!!!" and terminate the function
Step 5: If both are not matching, then check whether the search element is smaller or larger than middle
element.
Step 6: If the search element is smaller than middle element, then repeat steps 2, 3, 4 and 5 for the left
sublist of the middle element.
Step 7: If the search element is larger than middle element, then repeat steps 2, 3, 4 and 5 for the right
sublist of the middle element.
Step 8: Repeat the same process until we find the search element in the list or until sublist contains only
one element.
Step 9: If that element also doesn't match with the search element, then display "Element not found in
the list!!!" and terminate the function.
example
#include <stdio.h>
int main()
{
int c, first, last, middle, n, search, array[100];
first = 0;
last = n - 1;
middle = (first+last)/2;
return 0;
}
Time Complexity :
Best case:o(1)