Data Structures and Algorithms PDF
Data Structures and Algorithms PDF
UNIT 1
Abstract Data Types (ADTs) – ADTs and classes – introduction to OOP – classes in Python – inheritance
– namespaces – shallow and deep copying. Introduction to analysis of algorithms – asymptotic notations
– recursion – analyzing recursive algorithms
UNIT 2
Linear Structures- List ADT – array-based implementations – linked list implementations – singly linked
lists – circularly linked lists – doubly linked lists – applications of lists – Stack ADT – Queue ADT –
double ended queues
UNIT 3
Sorting and Searching-Bubble sort – selection sort – insertion sort – merge sort – quick sort – linear
search – binary search – hashing – hash functions – collision handling – load factors, rehashing, and
efficiency 15
UNIT 4
Tree Structures - Tree ADT – Binary Tree ADT – tree traversals – binary search trees – AVL trees – heaps
– multi-way search trees. 1
UNIT 5
Graph Structures- Graph ADT – representations of graph – graph traversals – DAG – topological ordering
– shortest paths – minimum spanning trees.
Textbooks
3. Michael T. Goodrich, Roberto Tamassia, and Michael H. Goldwasser, ―Data Structures & Algorithms
in Python‖, John Wiley & Sons Inc., 2013
4. Lee, Kent D., Hubbard, Steve, ―Data Structures and Algorithms with Python‖ Springer Edition 2015
5. Aho, Hopcroft, and Ullman, ―Data Structures and Algorithms‖, Pearson Education, 1983
Reference Books
1.Jean-Paul, Tremblay & Paul G .Sorenson , An Introduction to Data structures with Applications Tata
McGraw Hill Company 2008, 2ndEdition.
2. Samanta.D , Classic Data Structure Prentice Hall of India Pvt Ltd 2007, 9th Edition
3. Seymour Lipschutz, Data Structures McGraw Hill Publications, 2014, 1st Edition
4. Rance D. Necaise, ―Data Structures and Algorithms Using Python‖, John Wiley & Sons, 2011
5. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, ―Introduction to
Algorithms", Second Edition, McGraw Hill, 2002.
Web Resources
1. https://www.geeksforgeeks.org/data-structures/
2. https://www.tutorialspoint.com/data_structures_algorithms/index.htm
3. https://techdevguide.withgoogle.com/paths/data-structures-and-algorithms/
4. https://www.freecodecamp.org/news/learn-data-structures-and-algorithms/
5. https://www.worldscientific.com/worldscibooks/10.1142/5256#t=aboutBook
1.INDRODUCTION:
What is Data Structure:
A data structure is a storage that is used to store and organize data. It is a way of arranging
data on a computer so that it can be accessed and updated efficiently.
A data structure is not only used for organizing the data. It is also used for processing, retrieving,
and storing data.
There are different basic and advanced types of data structures that are used in almost every
program or software system that has been developed.
So we must have good knowledge about data structures.
Linear data structure: Data structure in which data elements are arranged sequentially or
linearly, where each element is attached to its previous and next adjacent elements, is
called a linear data structure.
Examples of linear data structures are array, stack, queue, linked list, etc.
Static data structure: Static data structure has a fixed memory size. It is easier to
access the elements in a static data structure.
An example of this data structure is an array.
Dynamic data structure: In dynamic data structure, the size is not fixed. It can be
randomly updated during the runtime which may be considered efficient concerning
the memory (space) complexity of the code.
Examples of this data structure are queue, stack, etc.
Non-linear data structure: Data structures where data elements are not placed
sequentially or linearly are called non-linear data structures. In a non-linear data structure,
we can’t traverse all the elements in a single run only.
Examples of non-linear data structures are trees and graphs.
Abstract Data type (ADT) is a type (or class) for objects whose behavior
is defined by a set of values 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. Think of ADT as a black box which hides the inner structure and
design of the data type. Now we’ll define three ADTs
namely List ADT, Stack ADT, Queue ADT.
1. List ADT
Vies of list
The data is generally stored in key sequence in a list which has a head
structure consisting of count, pointers and address of compare
function needed to compare the data in the list.
The data node contains the pointer to a data structure and a self-referential
pointer which points to the next node in the list.
The List ADT Functions is given below:
get() – Return an element from the list at any given position.
insert() – Insert an element at any position of the list.
remove() – Remove the first occurrence of any element from a non-empty
list.
removeAt() – Remove the element at a specified location from a non-empty
list.
replace() – Replace an element at any position by another element.
size() – Return the number of elements in the list.
isEmpty() – Return true if the list is empty, otherwise return false.
isFull() – Return true if the list is full, otherwise return false.
2. Stack ADT
View of stack
In Stack ADT Implementation instead of data being stored in each node, the
pointer to data is stored.
The program allocates memory for the data and address is passed to the
stack ADT.
The head node and the data nodes are encapsulated in the ADT. The calling
function can only see the pointer to the stack.
The stack head structure also contains a pointer to top and count of number
of entries currently in stack.
push() – Insert an element at one end of the stack called top.
pop() – Remove and return the element at the top of the stack, if it is not
empty.
peek() – Return the element at the top of the stack without removing it, if the
stack is not empty.
size() – Return the number of elements in the stack.
isEmpty() – Return true if the stack is empty, otherwise return false.
isFull() – Return true if the stack is full, otherwise return false.
3. Queue ADT
View of Queue
The queue abstract data type (ADT) follows the basic design of the stack
abstract data type.
Each node contains a void pointer to the data and the link pointer to the next
element in the queue. The program’s responsibility is to allocate memory for
storing the data.
enqueue() – Insert an element at the end of the queue.
dequeue() – Remove and return the first element of the queue, if the queue
is not empty.
peek() – Return the element of the queue without removing it, if the queue is
not empty.
size() – Return the number of elements in the queue.
isEmpty() – Return true if the queue is empty, otherwise return false.
isFull() – Return true if the queue is full, otherwise return false.
Features of ADT:
Abstract data types (ADTs) are a way of encapsulating data and operations on that
data into a single unit. Some of the key features of ADTs include:
Abstraction: The user does not need to know the implementation of the data
structure only essentials are provided.
Better Conceptualization: ADT gives us a better conceptualization of the real
world.
Robust: The program is robust and has the ability to catch errors.
Encapsulation: ADTs hide the internal details of the data and provide a public
interface for users to interact with the data. This allows for easier maintenance and
modification of the data structure.
Data Abstraction: ADTs provide a level of abstraction from the implementation
details of the data. Users only need to know the operations that can be performed on
the data, not how those operations are implemented.
Data Structure Independence: ADTs can be implemented using different data
structures, such as arrays or linked lists, without affecting the functionality of the
ADT.
Information Hiding: ADTs can protect the integrity of the data by allowing access
only to authorized users and operations. This helps prevent errors and misuse of the
data.
Modularity: ADTs can be combined with other ADTs to form larger, more complex
data structures. This allows for greater flexibility and modularity in programming.
Overall, ADTs provide a powerful tool for organizing and manipulating data in a
structured and efficient manner.
Abstract data types (ADTs) have several advantages and disadvantages that should be
considered when deciding to use them in software development. Here are some of the
main advantages and disadvantages of using ADTs:
Advantages:
Disadvantages:
OOPs
In the above example, we have created a class named Dog using the class
keyword.
Python
# Python3 program to
# demonstrate defining
# a class
class Dog:
pass
Inheritance in Python
In the above article, we have created two classes i.e. Person (parent class) and
Employee (Child Class).
The Employee class inherits from the Person class. We can use the methods of
the person class through the employee class as seen in the display function in the
above code.
A child class can also modify the behavior of the parent class as seen through the
details() method.
Python3
# are called.
# parent class
class Person(object):
self.idnumber = idnumber
def display(self):
print(self.name)
print(self.idnumber)
def details(self):
print("IdNumber: {}".format(self.idnumber))
# child class
class Employee(Person):
self.salary = salary
self.post = post
print("IdNumber: {}".format(self.idnumber))
print("Post: {}".format(self.post))
# its instance
a.display()
a.details()
Output
Rahul
886012
My name is Rahul
IdNumber: 886012
Post: Intern
1.3.2 NAMESPACESES IN PYTHON:
What is namespace:
A namespace is a system that has a unique name for each and every
object in Python.
An object might be a variable or a method. Python itself maintains a
namespace in the form of a Python dictionary.
Let’s go through an example, a directory-file system structure in
computers.
Needless to say, that one can have multiple directories having a file with
the same name inside every directory.
But one can get directed to the file, one wishes, just by specifying the
absolute path to the file.
Real-time example, the role of a namespace is like a surname. One might
not find a single “Alice” in the class there might be multiple “Alice” but
when you particularly ask for “Alice Lee” or “Alice Clark” (with a surname),
there will be only one (time being don’t think of both first name and
surname are same for multiple students).
On similar lines, the Python interpreter understands what exact method or
variable one is trying to point to in the code, depending upon the
namespace.
So, the division of the word itself gives a little more information.
Its Name (which means name, a unique identifier) + Space(which talks
something related to scope).
Here, a name might be of any Python method or variable and space
depends upon the location from where is trying to access a variable or a
method.
Types of namespaces :
Example:
Python3
var1 = 5
def some_func():
var2 = 6
def some_inner_func():
# namespace
var3 = 7
As shown in the following figure, the same object name can be present in
multiple namespaces as isolation between the same name is maintained by
their namespace.
# global variable
count = 5
def some_method():
global count
count = count + 1
print(count)
some_method()
Output:
6
Shallow Copy:
Shallow repetition is quicker. However, it’s “lazy” it handles pointers and
references. Rather than creating a contemporary copy of the particular
knowledge the pointer points to, it simply copies over the pointer price.
So, each of the first and therefore the copy can have pointers that reference
constant underlying knowledge.
Deep Copy:
Deep repetition truly clones the underlying data. It is not shared between the
first and therefore the copy.
Difference between Shallow and Deep copy of a class
Shallow Copy reflects changes made to the Deep copy doesn’t reflect changes made to the
new/copied object in the original object. new/copied object in the original object.
Shallow Copy stores the copy of the original Deep copy stores the copy of the original object
object and points the references to the objects. and recursively copies the objects as well.
Definition of Algorithm
CHARACTERISTICS OF AN ALGORITHM
a) Input
b) Output
c) Finiteness
d) Definiteness
e) Effectiveness
f) Precision
g) Determination
h) Correctness
i) Generality
a) Input
An algorithm may have one or more inputs. The inputs are taken from a specified set of subjects.
The input pattern may be texts, images or any type of files
b) Output :
An algorithm may have one or more outputs. Output is basically a quantity which has a specified
relation with the input
c) Finiteness:
An algorithm should terminate after a countable number of steps. In some cases the repetition of
steps may be larger in number. If a procedure is able to resolve in finite number of execution of steps,
then it is referred to be computational method.
d) Definiteness:
Each step of an algorithm must be precisely defined. The action to be carried out must be on
ambiguously specified for each case. Due to the lack of understandability one may think that the step
might be lacking definiteness. Therefore in such cases mathematically expressions are written, so that it
resembles the instruction of any computer language.
e) Effectiveness :
An algorithm is generally expected to effective. Means the steps should be sufficiently basic, so that it
may be possible for a man to resolve them.
g) Determination: The intermediate results of each step of execution are unique and are determined
only by input and output of the preceding steps.
Examples :
{ 100 , log (2000) , 10^4 } belongs to O(1)
U { (n/4) , (2n+3) , (n/100 + log(n)) } belongs to O(n)
U { (n^2+n) , (2n^2) , (n^2+log(n))} belongs to O( n^2)
Note: Here, U represents union, we can write it in these manner because O
provides exact or upper bounds .
Let g and f be the function from the set of natural numbers to itself. The function
f is said to be Ω(g), if there is a constant c > 0 and a natural number n0 such
that c*g(n) ≤ f(n) for all n ≥ n0
Mathematical Representation of Omega notation :
Ω(g(n)) = { f(n): there exist positive constants c and n0 such that 0 ≤ cg(n) ≤ f(n)
for all n ≥ n0 }
Let us consider the same Insertion sort example here. The time complexity of
Insertion Sort can be written as Ω(n), but it is not very useful information about
insertion sort, as we are generally interested in worst-case and sometimes in
the average case.
Examples :
{ (n^2+n) , (2n^2) , (n^2+log(n))} belongs to Ω( n^2)
U { (n/4) , (2n+3) , (n/100 + log(n)) } belongs to Ω(n)
U { 100 , log (2000) , 10^4 } belongs to Ω(1)
Note: Here, U represents union, we can write it in these manner because Ω
provides exact or lower bounds.
Introduction to Recursion :
What is Recursion?
The process in which a function calls itself directly or indirectly is called
recursion and the corresponding function is called a Recursive function. Using
a recursive algorithm, certain problems can be solved quite easily.
Need of Recursion
Recursion is an amazing technique with the help of which we can reduce
the length of our code and make it easier to read and write. It has certain
advantages over the iteration technique which will be discussed later.
A task that can be defined with its similar subtask, recursion is one of the
best solutions for it. For example; The Factorial of a number.
Properties of Recursion:
Performing the same operations multiple times with different inputs.
In every step, we try smaller inputs to make the problem smaller.
Base condition is needed to stop the recursion otherwise infinite loop will
occur
SR
Recursion Iteration
No.
Every recursive call needs extra space Every iteration does not require any
3)
in the stack memory. extra space.
Recurrence Relation:
T(n) = T(n-1) + T(n-2) + O(1)
Recursive program:
Input: n = 5
Output:
Fibonacci series of 5 numbers is : 0 1 1 2 3
Base Case: It is nothing more than the simplest instance of a problem, consisting of a
condition that terminates the recursive function. This base case evaluates the result when a
given condition is met.
Recursive Step: It computes the result by making recursive calls to the same function, but
with the inputs decreased in size or complexity.
For example, consider this problem statement: Print sum of n natural numbers using
recursion. This statement clarifies that we need to formulate a function that will calculate
the summation of all natural numbers in the range 1 to n. Hence, mathematically you
can represent the function as:
There are four different types of recursive algorithms, you will look at them one by one.
Direct Recursion
A function is called direct recursive if it calls itself in its function body repeatedly. To
better understand this definition, look at the structure of a direct recursive program.
In this program, you have a method named fun that calls itself again in its function body.
Thus, you can say that it is direct recursive.
Indirect Recursion
The recursion in which the function calls itself via another function is called indirect
recursion. Now, look at the indirect recursive program structure.
fun2(z-1); fun1(y-2)
} }
In this example, you can see that the function fun1 explicitly calls fun2, which is invoking
fun1 again. Hence, you can say that this is an example of indirect recursion.
Tailed Recursion
A recursive function is said to be tail-recursive if the recursive call is the last execution
done by the function. Let’s try to understand this definition with the help of an example.
int fun(int z)
printf(“%d”,z);
fun(z-1);
}
If you observe this program, you can see that the last line ADI will execute for method
fun is a recursive call. And because of that, there is no need to remember any previous
state of the program.
Non-Tailed Recursion
A recursive function is said to be non-tail recursive if the recursion call is not the last
thing done by the function.
After returning back, there is something left to evaluate. Now, consider this example.
int fun(int z)
fun(z-1);
printf(“%d”,z);
In this function, you can observe that there is another operation after the recursive call.
Hence the ADI will have to memorize the previous state inside this method block. That
is why this program can be considered non-tail recursive.
Moving forward, you will implement a C program that exhibits recursive algorithmic
nature.
Program to Demonstrate Recursion:
You will look at a C program to understand recursion in the case of the sum of n natural
numbers problem.
#include<stdio.h>
if(n==0){
return 0;
return n + temp;
int main()
int n;
scanf("%d",&n);
printf("%d",Sum(n));
}
Output:
The output for the sum of n natural numbers program is represented in the image below.
IMPOTANT QUESTIONS: