0% found this document useful (0 votes)
10 views

Data Structures Using Python

The document outlines a syllabus for a course on Data Structures using Python, covering topics such as Object-Oriented Programming (OOP) concepts, various data structures including linked lists, trees, and graphs, as well as algorithms for searching and sorting. It details the fundamentals of classes, objects, inheritance, encapsulation, polymorphism, and abstraction in Python, along with examples and explanations of different types of methods and variables. Additionally, it provides a brief overview of linked lists and includes a simple Python program to demonstrate their implementation.

Uploaded by

nithin raj
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Data Structures Using Python

The document outlines a syllabus for a course on Data Structures using Python, covering topics such as Object-Oriented Programming (OOP) concepts, various data structures including linked lists, trees, and graphs, as well as algorithms for searching and sorting. It details the fundamentals of classes, objects, inheritance, encapsulation, polymorphism, and abstraction in Python, along with examples and explanations of different types of methods and variables. Additionally, it provides a brief overview of linked lists and includes a simple Python program to demonstrate their implementation.

Uploaded by

nithin raj
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 111

DATA STRUCTURES USING PYTHON

SYLLUBUS

UNIT – I OOP Concepts - Class, Object, Types of variables, Types of methods,


Inheritance, Encapsulation, Polymorphism, Abstraction, Special functions,
Constructors. Data Structures: Types-User Define, Predefine, Comprehension
and its types, Arrays vs. List.

UNIT – II Linked List ADT - Singly Linked Lists - Doubly Linked Lists - Circular
Linked Lists – Stacks ADT - Queues implementation – Dequeues (Concept).

UNIT - III Searching: Linear Search - Binary search. Sorting: Merge Sort - Bubble
Sort - Selection Sort - Insertion Sort – Quick Sort (Concepts and
Implementation).

UNIT - IV Trees: Binary Tree - Binary search trees: find, insert, and delete -
Height-balanced binary search trees - Tree Traversal Algorithms (Inorder,
Preorder, Postorder Traversal)

UNIT -V Graphs: Data Structures for Graphs - Graph Traversals: Breadth First
Search, Depth First Search - Directed Acyclic Graphs - Shortest Paths
Object-oriented programming

• Object-oriented programming mimics a lot of the real-world attributes of objects.

• Some of the most widely used object-oriented programming languages are Java, C++, and
Ruby.

• These objects contain data, which we also refer to as attributes or properties, and methods.

• Objects can interact with each other.

• In object-oriented programming data structures or objects are defined, each with its own
properties or attributes

. • Each object can also contain its own procedures or methods.

• Software is designed by using objects that interact with one another. This offers various
benefits, like:
• being faster and easier to execute;
• providing a clear structure for a program;
• making code easier to modify, debug and maintain; and
• Making it easier to reuse code.
• Better able to create GUI (graphical user interface) applications.
• Programmers are able to produce faster, more accurate and better Applications.

Main Concepts of Object-Oriented Programming (OOPs)

• Class
• Objects
• Inheritance
• Encapsulation
• Polymorphism
• Data Abstraction

.
Class :
A class is a collection of objects. A class contains the blueprints or the
prototype from which the objects are being created. It is a logical entity that
contains some attributes and methods.

Some points on Python class:

• Classes are created by keyword class.


• Attributes are the variables that belong to a class.
• Attributes are always public and can be accessed using the dot (.) operator.
Eg.: Myclass.Myattribute

Class Definition Syntax:


class ClassName:
# Statement-
.
.
# Statement-N
Example: Creating an empty Class

# Python3 program to
# demonstrate defining
# a class

class Student:
pass

In the above example, we have created a class named Student using the class
keyword.

Object:
An object (instance) is an instantiation of a class. When class is defined, then only
the description for the object is defined. Therefore, no memory or storage is
allocated.

• The entire OOPS methodology has been derived from a single root concept
called ‘Object’.
• An object is anything that really exists in the world and can be distinguished
from others.
• Ex: Table, Cat, dog and Person
• Every object has some behaviour.
• Behaviour of an object is represented by attributes and actions.

Syntax:
objectname= classname()

• The example for object of parrot class can be:


obj = Parrot()
• obj is an object of class Parrot

Example:
std = Student()
➢ Accessing data members from Student class

print(“value of a=“,std.a)
The self :
1. Class methods must have an extra first parameter in the method definition.
We do not give a value for this parameter when we call the method, Python
provides it
2. If we have a method that takes no arguments, then we still have to have one
argument.

The General Format of Class:

Class classname(object):

“”” docstring describing the class”””

def __init__(self):

def method1():

def method2():

*A class consists of 4 key members:

• Constructor: The constructor must have a special name __init__() and a special

parameter called self. In Python, the constructor is invoked automatically


whenever a new object of a class is instantiated.

The task of the constructor is to initialize the data members of the class when
object is created.

Syntax:

def __init__(self,parameter1,parameter2,.......parameter n):

self.a=parameter1

• Class variable / attribute: A class variable contains information that is


shared by all objects of the class

• Instance variable / Instance attribute: An attribute that is created per


instance/object.
• Method(s): A function that can access any of these variables.

Write a simple python program using class, object and constructor:

class Student:

def __init__(self, name, age): # class constructor

self.sname = name

self.sage = age

def displaystdinfo(self): # class method or function

print(‘student Name: ', self.sname)

print(“student Age:”, self.sage)

std = Student(‘Ramu', 25) # class object ‘std’ is creating

std.displaystdinfo() # displaystdinfo() is calling by using ‘std’ object

Output:

student Name: Ramu

student Age: 25
To summarize
A constructor is called if you
create an object.
In the constructor you can set
variables and
call methods.

Types of Variables:
The variables which are written inside a class are 3 types:

1.Static variables or class variables.

2.Instance variables.

3.Local variables.

Types of methods:
• The purpose of method is to process the variables provided in class or in
method.

• The variables declared in class called class variables, variables declared in


constructor

are called instance variables.

• We can classify the methods in 3 types:

1. Instance Methods

2. Class Methods
3. Static Methods

1.Instance Method:
• Instance methods are methods which act upon the instance variables.

• Instance methods are bound to instances or objects and hence called as

instancename.method().

• Instances methods need to know the memory address of the instance.

• This is provided by self variable by default.

• While calling the instance methods we need not pass any value to self variable.

• #instance methods to process data of the objects

Example:
class sample:
#Calling constructor
def __init__(self,a,b):
print(“Hello”)
self.a=a
self.b=b
#Instance Method
def display(self):
print(self.a,self.b)
S1=sample(10,20)
S1.display()

Output:
10 20

2.Class Method:
➢ Using @classmethod decorator we can create class method. The class
method and instance method both are same except some modifications i.e
➢ To make a instance method as class method by add @classmethod
decorator before the method definition, and add cls as the first parameter
to that method means ‘self’ parameter is replaced by ‘cls’ parameter.

*Class methods can be called by both class_name and object.


➢ Syntax:
# class method
@classmethod
def display(cls,parameter1,parameter2):
statement-1
statement-2

Example python program using Class Method:


class Student:
@classmethod
def age(cls):
cls.sage=20
print("Student age=",cls.sage)
f=Student()
f.age() # class method is called by using object

Output:
Student age= 20

3.Static Method:
➢ Using @staticmethod decorator we can create static method.

➢ add @staticmethod decorator before the method definition.

➢ A class method takes cls as the first parameter while a static method needs
no specific parameters.

➢ Static methods can be called by both class_name and object.

➢ Syntax:

# static method
@staticmethod
def display( ):
statement-1
statement-2
Example program using static method:
class Student:
@staticmethod
def age():
sage=20
print("Student age=",sage)
f=Student()
f.age() # call by using object
Student.age() # call by using Class_name
.
Output:
Student age= 20
Student age= 20

Inheritance :
Inheritance is the process of deriving a new class from an existing class.

➢ The existing class is called the parent class, or superclass, or base class

➢ The new class is called the child class or subclass, or derived class.

➢ The child class inherits properties from the parent class(i.e the child class
inherits the methods and data from the parent class) .
Fig: Inheritance

Types of inheritances
There are five types of inheritances:

➢ Simple Inheritance (or) Single Inheritance

➢ Multiple Inheritance

➢ Multi-Level Inheritance

➢ Hierarchical Inheritance

➢ Hybrid Inheritance
*Single inheritance : one child class derived properties from one parent
class

Syntax:

class parent_class_name:
------------------

multiple statements (either data members or methods)

---------------------

class child_class_name (parent_class_name):

------------------

multiple statements (either data members or methods)

---------------------

Note: child class object is enough for accessing both child class properties and
parent class properties.

Example program :

class A: #parent class


def display1(self):
print("Hi")
def display2(self):
print("Hello")
class B(A): # child class with parent class
def display3(self):
print("ramu")
s=B() #child class object is created
s.display1()
s.display2()
s.display3()

Output:
Hi
Hello
Ramu

*Multiple inheritance : one child class derived properties from two or


more parent classes
Syntax:
class parent_class_name1:
------------------
multiple statements (either data members or methods)
---------------------
class parent_class_name2:
------------------
multiple statements (either data members or methods)
---------------------
class child_class_name (parent_class_name1,parent_class_name2):
------------------
multiple statements (either data members or methods)
---------------------
Note: If same method or variable is declared in both parent classes, then the child
class accessing only the first declared parent class method. This is the problem
raising in multiple inheritance.

Example program :
class A:

def display1(self):

print("Hi")

class C:

def display2(self):

print("Hello")

class B(A,C):

def display3(self):

print("ramu")

s=B()

s.display1()
s.display2()

s.display3()

Output:

Hi
Hello
ramu

*Multilevel Inheritance:
one child class derives from a class which is already derived from another class

Example program using Multilevel inheritance:

class Family:
def show_family(self):
print("This is our family")

# Father class inherited from Family


class Father(Family):
fathername="ramu"
def show_father(self):
print(self.fathername)

# Son class inherited from Father class


class Son(Father):
def show_parent(self):
print("Hi")

s1 = Son() # Object of Son class


s1.show_parent()
s1.show_father()
s1.show_family()

Output:
Hi
ramu
This is our family

*Hierarchical Inheritance:
Two or more child classes derives from one parent class

Example program using Multilevel inheritance:


class A:
def display1(self):
print("Hi")

class B(A):
def display2(self):
print("Hello")
class C(A):
def display3(self):
print("Hello")

s=B()
s1=C()
s.display1()
s1.display1()

OUTPUT:
Hi
Hi

*Hybrid Inheritance:
It is the combination of all the inheritances
Example:
class Class1: # parent class
def m(self):
print("In Class1")

class Class2(Class1):
def m(self):
print("In Class2")

class Class3(Class1):
def m(self):
print("In Class3")

class Class4(Class2, Class3):


pass

obj = Class4()
obj.m()
Output:
In Class2
ENCAPSULATION:
• Encapsulation is the packing of data and functions that work on that data within
a single object.
• We can hide the internal state of the object from the outside. This is known
as information hiding.
• A class is an example of encapsulation.

EXAMPLE:
class A():
def __init__(self):
self._a=10
self.__b=20
self.c=30
def display(self):
print(self._a)
print(self.__b)
print(self.c)
class B(A):
def __init__(self):
super().__init__()
def display(self):
print(self._a)
print(self.c)
class c():
def __init__(self):
self.d=40
def display(self):
print(self.d)
a1=A()
a1.display()
b1=B()
b1.display()
c1=c()
c1.display()

OUTPUT:
10
20
30
10
30
40

Polymorphism:
The word polymorphism means having many forms. In programming,
polymorphism means the same function name (but different
signatures) being used for different types.
Example:
class sample:
def add(self,a,b,c=0,d=0):
print(a+b+c+d)
s1=sample()
s1.add(1,2,3,4)
s1.add(3,4)
s1.add(1,2,3)
Output:
10
7
6

Abstraction:
Abstraction is used to hide the internal functionality of the function from the users.
An abstraction is used to hide the irrevalent data/class in order to reduce the
complexity.
Abstraction classes in python:
In python,abstraction can be achieved by using abstraction classes and interfaces.
Example:
from abc import abstractmethod,ABC
class polygon(ABC):
@abstractmethod
def sides(self):
pass
class triangle(polygon):
def sides(self):
self.s=3
def display(self):
print(self.s)
t1=triangle()
t1.sides()
t1.display()

Output:
3
UNIT – II

Linked Lists:

Linked lists are one of the most commonly used data structures in any programming
language. Linked Lists, on the other hand, are different. Linked lists, do not store data at
contiguous memory locations. For each item in the memory location, linked list stores value
of the item and the reference or pointer to the next item. One pair of the linked list item and
the reference to next item constitutes a node.

The following are different types of linked lists.

• Single Linked List


A single linked list is the simplest of all the variants of linked lists. Every node in a single
linked listcontains an item and reference to the next item and that's it.

• Doubly Linked List


• Circular Linked List
• Linked List with Header

# Python program to create a linked list and display its elements.

The program creates a linked list using data items input from the user and displays it.

Solution:
1. Create a class Node with instance variables data and next.
2. Create a class Linked List with instance variables head and last_node.
3. The variable head points to the first element in the linked list while last_node points to the
last.
4. Define methods append and display inside the class Linked List to append data and display
the linked listrespectively.
5. Create an instance of Linked List, append data to it and display the list.

30
Program:
class Node:
def init (self, data):
self.data= data
self.next=None

class LinkedList:
def init (self):
self.head=None
self.last_node=None

def append(self,data):
ifself.last_nodeisNone:
self.head= Node(data)
self.last_node=self.head
else:
self.last_node.next= Node(data)
self.last_node=self.last_node.next

def display(self):
current =self.head
while current isnot None:
print(current.data, end =' ')
current = current.next

a_llist = LinkedList()
n =int(input('How many elements would you like to add? '))
for i in range(n):
data =int(input('Enter data item: '))
a_llist.append(data)
print('The linked list: ', end ='')
a_llist.display()

Program Explanation
1. An instance of Linked List is created.
2. The user is asked for the number of elements they would like to add. This is stored in n.
3. Using a loop, data from the user is appended to the linked list n times.
4. The linked list is displayed.

31
Output:
How many elements would you like to add? 5
Enter data item: 4
Enter data item: 4
Enter data item: 6
Enter data item: 8
Enter data item: 9
The linked list: 4 4 6 8 9

Doubly Linked List

A doubly linked list is a linked data structure that consists of a set of sequentially linked
records callednodes.
Each node contains three fields: two link fields (references to the previous and to the next
node in thesequence of nodes) and one data field.

Advantages of Doubly Linked List

1. Traversal can be done on either side means both in forward as well as backward.
2. Deletion Operation is more efficient if the pointer to delete node is given.

Disadvantages of Linked List


1. Since it requires extra pointer that is the previous pointer to store previous node reference.
2. After each operation like insertion-deletion, it requires an extra pointer that is a previous
pointer whichneeds to be maintained.

So, a typical node in the doubly linked list consists of three fields:

o Data represents the data value stored in the node.


o Previous represents a pointer that points to the previous node.
o Next represents a pointer that points to the next node in the list.

The above picture represents a doubly linked list in which each node has two pointers to
point to previous and next node respectively. Here, node 1 represents the head of the list.
The previous pointer of the head node will always point to NULL. Next pointer of node one
will point to node 2. Node 5 represents the tail of the list whose previous pointer will point
to node 4, and the next will point to NULL.

32
ALGORITHM:

1. Define a Node class which represents a node in the list. It will have three properties: data,
previouswhich will point to the previous node and next which will point to the next node.

2. Define another class for creating a doubly linked list, and it has two nodes: head and tail.
Initially,head and tail will point to null.

3. addNode() will add node to the list:

• It first checks whether the head is null, then it will insert the node as the head.
• Both head and tail will point to a newly added node.
• Head's previous pointer will point to null and tail's next pointer will point to null.
• If the head is not null, the new node will be inserted at the end of the list such
that new node'sprevious pointer will point to tail.
• The new node will become the new tail. Tail's next pointer will point to null.
• a.display() will show all the nodes present in the list.
• Define a new node 'current' that will point to the head.
• Print current.data till current points to null.
• Current will point to the next node in the list in each iteration.

PROGRAM:
1. #Represent a node of doubly linked list
2. class Node:
3. def __init (self,data):
4. self.data = data;
5. self.previous = None;
6. self.next = None;7.
8. class DoublyLinkedList:
9. #Represent the head and tail of the doubly linked list
10. def __init (self):
11. self.head = None;
12. self.tail = None;13.
14. #addNode() will add a node to the list
15. def addNode(self, data):
16. #Create a new node
17. newNode = Node(data);18.
19. #If list is empty
20. if(self.head == None):
21. #Both head and tail will point to newNode
22. self.head = self.tail = newNode;
23. #head's previous will point to None
24. self.head.previous = None;
33
25. #tail's next will point to None, as it is the last node of the list
26. self.tail.next = None;
27. else:
28. #newNode will be added after tail such that tail's next will point to newNode
29. self.tail.next = newNode;
30. #newNode's previous will point to tail
31. newNode.previous = self.tail;
32. #newNode will become new tail
33. self.tail = newNode;
34. #As it is last node, tail's next will point to None
35. self.tail.next = None;36.
37. #display() will print out the nodes of the list
38. def display(self):
39. #Node current will point to head
40. current = self.head;
41. if(self.head == None):
42. print("List is empty");
43. return;
44. print("Nodes of doubly linked list: ");
45. while(current != None):
46. #Prints each node by incrementing pointer.
47. print(current.data),;
48. current = current.next;49.
50. dList = DoublyLinkedList();
51. #Add nodes to the list
52. dList.addNode(1);
53. dList.addNode(2);
54. dList.addNode(3);
55. dList.addNode(4);
56. dList.addNode(5);57.
58. #Displays the nodes present in the list
59. dList.display();

Output:

Nodes of doubly linked list:


12345

Circular Linked List

The circular linked list is a kind of linked list. First thing first, the node is an element of the
list, and it has two parts that are, data and next. Data represents the data stored in the node,
and next is the pointer that will point to the next node. Head will point to the first element of

34
the list, and tail will point to the last element in the list. In the simple linked list, all the nodes
will point to their next element and tail will point to null.

The circular linked list is the collection of nodes in which tail node also point back to head
node. The diagram shown below depicts a circular linked list. Node A represents head and
node D represents tail. So, in this list, A is pointing to B, B is pointing to C and C is pointing
to D but what makes it circular is that node D is pointing back to node A.

ALGORITHM:
1. Define a Node class which represents a node in the list. It has two properties data and
next whichwill point to the next node.
2. Define another class for creating the circular linked list, and it has two nodes: head and tail.
It hastwo methods: add() and display() .
3. add() will add the node to the list:
• It first checks whether the head is null, then it will insert the node as the head.
• Both head and tail will point to the newly added node.
• If the head is not null, the new node will be the new tail, and the new tail will point to
the head as itis a circular linked list.
• a.display() will show all the nodes present in the list.
• Define a new node 'current' that will point to the head.
• Print current.data till current will points to head
• Current will point to the next node in the list in each iteration.

PROGRAM:
1. #Represents the node of list.
2. class Node:
3. def __init (self,data):
4. self.data = data;
5. self.next = None;

6. class CreateList:
7. #Declaring head and tail pointer as null.
8. def __init (self):
9. self.head = Node(None);
35
10. self.tail = Node(None);
11. self.head.next = self.tail;
12. self.tail.next = self.head;14.
13. #This function will add the new node at the end of the list.
14. def add(self,data):
15. newNode = Node(data);
16. #Checks if the list is empty.
17. if self.head.data is None:
18. #If list is empty, both head and tail would point to new node.
19. self.head = newNode;
20. self.tail = newNode;
21. newNode.next = self.head;
22. else:
23. #tail will point to new node.
24. self.tail.next = newNode;
25. #New node will become new tail.
26. self.tail = newNode;
27. #Since, it is circular linked list tail will point to head.
28. self.tail.next = self.head;31.
29. #Displays all the nodes in the list
30. def display(self):
31. current = self.head;
32. if self.head is None:
33. print("List is empty");
34. return;
35. else:
36. print("Nodes of the circular linked list: ");
37. #Prints each node by incrementing pointer.
38. print(current.data),
39. while(current.next != self.head):
40. current = current.next;
41. print(current.data)

42. class CircularLinkedList:


36
43. cl = CreateList();
44. #Adds data to the list
45. cl.add(1);
46. cl.add(2);
47. cl.add(3);
48. cl.add(4);
49. #Displays all the nodes present in the list
50. cl.display();

Output:

Nodes of the circular linked list:


1234

Stacks:

Stack works on the principle of “Last-in, first-out”. Also, the inbuilt functions in Python make
the code short and simple. To add an item to the top of the list, i.e., to push an item, we use
append() function and to pop out an element we use pop() function.

#Python code to demonstrate Implementing stack using list

stack = ["Amar", "Akbar", "Anthony"]


stack.append("Ram")
stack.append("Iqbal")
print(stack)
print(stack.pop())
print(stack)
print(stack.pop())
print(stack)

Output:
['Amar', 'Akbar', 'Anthony', 'Ram', 'Iqbal']
Iqbal
['Amar', 'Akbar', 'Anthony', 'Ram']
Ram
['Amar', 'Akbar', 'Anthony']

37
Stack Using Linked List
A stack using a linked list is just a simple linked list with just restrictions that any element
will be added andremoved using push and pop respectively. In addition to that, we also
keep top pointer to represent the top of the stack. This is described in the picture given
below.

Stack Operations:

1. push() : Insert the element into linked list nothing but which is the top node of Stack.
2. pop() : Return top element from the Stack and move the top pointer to the second node of
linked listor Stack.
3. peek(): Return the top element.
4. display(): Print all element of Stack.

IS_EMPTY(S)
if S.top == null
return TRUE
return FALSE

A stack will be empty if the linked list won’t have any node i.e., when the top pointer of the
linked list willbe null. So, let’s start by making a function to check whether a stack is empty
or not.

Now, to push any node to the stack (S) - PUSH(S, n), we will first check if the stack is empty
or not. If thestack is empty, we will make the new node head of the linked list and also point
the top pointer to it.

38
PUSH(S, n)
if IS_EMPTY(S) //stack is empty
S.head = n //new node is the head of the linked listS.top = n //new node is the also the top
If the stack is not empty, we will add the new node at the last of the stack. For that, we will
point next ofthe top to the new node - (S.top.next = n) and the make the new node top of the
stack - (S.top = n).

PUSH(S, n)
if IS_EMPTY(S) //stack is empty
...
else
S.top.next = nS.top = n

PUSH(S, n)
if IS_EMPTY(S) //stack is empty
S.head = n //new node is the head of the linked listS.top = n
//new node is the also the top
else
S.top.next = n
S.top = n

39
Similarly, to remove a node (pop), we will first check if the stack is empty or not as we did
in theimplementation with array.
POP(S)
if IS_EMPTY(S)
Error “Stack Underflow”
In the case when the stack is not empty, we will first store the value in top node in a
temporary variablebecause we need to return it after deleting the node.
POP(S)
if IS_EMPTY(S)
...
else
x = S.top.data
Now if the stack has only one node (top and head are same), we will just make both top and
head null.

POP(S)
if IS_EMPTY(S)
...
else
...
if S.top == S.head //only one nodeS.top = NULL
S.head = NULL
If the stack has more than one node, we will move to the node previous to the top node
and make the next ofpoint it to null and also point the top to it.

POP(S)
...
...
if S.top == S.head //only one node
...

40
else
tmp = S.head
while tmp.next != S.top //iterating to the node previous to toptmp = tmp.next
tmp.next = NULL //making the next of the node nullS.top = tmp //changing the top pointer
We first iterated to the node previous to the top node and then we marked its next to null -
tmp.next =NULL. After this, we pointed the top pointer to it - S.top = tmp.
At last, we will return the data stored in the temporary variable - return x.

POP(S)
if IS_EMPTY(S)
Error "Stack Underflow"else
x = S.top.data
if S.top == S.head //only one nodeS.top
= NULL
S.head = NULLelse
tmp = S.head
while tmp.next != S.top //iterating to the node previous to toptmp =
tmp.next
tmp.next = NULL //making the next of the node nullS.top = tmp
//changing the top pointer
return x

Program

class Node():
def __init__(self, data):
self.data = data self.next = None
class Stack():
def __init__(self):
self.head = Noneself.top = None
def traversal(s):
temp = s.head #temporary pointer to point to head
a = ''
while temp != None: #iterating over stacka = a+str(temp.data)+'\t'
temp = temp.next
print(a)
def is_empty(s):
if s.top == None:
return True return False
def push(s, n):
if is_empty(s): #emptys.head = n
s.top = nelse:
s.top.next = ns.top = n
def pop(s):
41
if is_empty(s):
print("Stack Underflow")return -1000
else:
x = s.top.data
if s.top == s.head: # only one nodes.top = None
s.head = Noneelse:
temp = s.head
while temp.next != s.top: #iterating to the last elementtemp = temp.next
temp.next = Nonedel s.top
s.top = temp
return x
if __name__ == '__main__':
s = Stack()
a = Node(10)b = Node(20)c = Node(30)
pop(s) push(s, a)
push(s, b)
push(s, c)
traversal(s)
pop(s)
traversal(s)

Applications of Stack
There are many applications of a stack. Some of them are:

• Stacks are used in backtracking algorithms.


• They are also used to implement undo/redo functionality in a software.
• Stacks are also used in syntax parsing for many compilers.
• Stacks are also used to check proper opening and closing of parenthesis.

Queue
Similar to stacks, a queue is also an Abstract Data Type or ADT. A queue follows FIFO (First-
in, First out) policy. It is equivalent to the queues in our general life. For example, a new
person enters a queue at the last and the person who is at the front (who must have entered
the queue at first) will be served first.

42
Similar to a queue of day to day life, in Computer Science also, a new element enters a queue
at the last (tailof the queue) and removal of an element occurs from the front (head of the
queue).

Similar to the stack, we will implement the queue using a linked list as well as with an array.
But let’s firstdiscuss the operations which are done on a queue.
Enqueue → Enqueue is an operation which adds an element to the queue. As stated earlier,
any new itementers at the tail of the queue, so Enqueue adds an item to the tail of a queue.

Dequeue → It is similar to the pop operation of stack i.e., it returns and deletes the front
element from thequeue.

isEmpty → It is used to check whether the queue has any element or not.
isFull → It is used to check whether the queue is full or not.
Front → It is similar to the top operation of a stack i.e., it returns the front element of the
queue (but don’tdelete it).
Before moving forward to code up these operations, let’s discuss the applications of a queue.

43
Applications of Queue

Queues are used in a lot of applications, few of them are:

• Queue is used to implement many algorithms like Breadth First Search (BFS), etc.
• It can be also used by an operating system when it has to schedule jobs with equal priority
• Customers calling a call center are kept in queues when they wait for someone to pick up
the callsQueue Using an Array

We will maintain two pointers - tail and head to represent a queue. head will always point
to the oldestelement which was added and tail will point where the new element is going to
be added.

To insert any element, we add that element at tail and increase the tail by one to point to
the next element ofthe array.

Suppose tail is at the last element of the queue and there are empty blocks before head as
shown in thepicture given below.

In this case, our tail will point to the first element of the array and will follow a circular
order.

44
Initially, the queue will be empty i.e., both head and tail will point to the same location i.e., at
index 1. We can easily check if a queue is empty or not by checking if head and tail are
pointing to the same location or not at any time.

IS_EMPTY(Q)
If Q.tail == Q.headreturn
True
return False

Similarly, we will say that if the head of a queue is 1 more than the tail, the queue is full.

IS_FULL(Q)
if Q.head = Q.tail+1
return True
Return False
Now, we have to deal with the enqueue and the dequeue operations.
To enqueue any item to the queue, we will first check if the queue is full or not i.e.,
Enqueue(Q, x)
if isFull(Q)
Error “Queue Overflow”
else…
45
If the queue is not full, we will add the element to the tail i.e, Q[Q.tail] = x.

While adding the element, it might be possible that we have added the element at the last
of the array and inthis case, the tail will go to the first element of the array.

Otherwise, we will just increase the tail by 1.

Enqueue(Q, x)
if isFull(Q)
Error “Queue Overflow”
else
Q[Q.tail] = x
if Q.tail == Q.size
Q.tail = 1
else
Q.tail = Q.tail+1

46
To dequeue, we will first check if the queue is empty or not. If the queue is empty, then we
will throw anerror.
Dequeue(Q, x) if isEmpty(Q)
Error “Queue Underflow”else

To dequeue, we will first store the item which we are going to delete from the queue in a
variable becausewe will be returning it at last.
Dequeue(Q, x) if isEmpty(Q)
Error “Queue Underflow”else
x = Q[Q.head]

Now, we just have to increase the head pointer by 1. And in the case when the head is at
the last element ofthe array, it will go 1.

Dequeue(Q, x) if
isEmpty(Q)
Error “Queue Underflow”else
x = Q[Q.head]
if Q.head == Q.sizeQ.head = 1
else
Q.head = Q.head+1return
x

Program
class Queue:
def __init__(self, size):self.head = 1
self.tail = 1
self.Q = [0]*(size)self.size = size
def is_empty(self):
if self.tail == self.head:
return True

47
return False
def is_full(self):
if self.head == self.tail+1:
return True
return False
def enqueue(self, x):
if self.is_full():
print("Queue Overflow")
else:
self.Q[self.tail] = x
if self.tail == self.size:
self.tail = 1
else:
self.tail = self.tail+1
def dequeue(self):
if self.is_empty():
print("Underflow")
else:
x = self.Q[self.head]
if self.head == self.size:
self.head = 1
else:
self.head = self.head+1
return x
def display(self):
i = self.head
while(i < self.tail):
print(self.Q[i])
if(i == self.size):
i=0
i = i+1
if __name__ == '__main__':
q = Queue(10)
q.enqueue(10)
q.enqueue(20)
q.enqueue(30)
q.enqueue(40)
q.enqueue(50)
q.display()
print("")
q.dequeue()
q.dequeue()

48
q.display()
print("")
q.enqueue(60)
q.enqueue(70)
q.display()

Queue Using Linked List

As we know that a linked list is a dynamic data structure and we can change the size of it
whenever it is needed. So, we are not going to consider that there is a maximum size of the
queue and thus the queue willnever overflow. However, one can set a maximum size to
restrict the linked list from growing more than that size.
As told earlier, we are going to maintain a head and a tail pointer to the queue. In the case
of an emptyqueue, head will point to NULL.

49
IS_EMPTY(Q)

if Q.head == null

return True

return False

We will point the head pointer to the first element of the linked list and the tail pointer to
the last element ofit as shown in the picture given below.

The enqueue operation simply adds a new element to the last of a linked list.

However, if the queue is empty, we will simply make the new node head and tail of the
queue.

50
ENQUEUE(Q, n) if
IS_EMPTY(Q)
Q.head = nQ.tail =
n
else
Q.tail.next = nQ.tail = n

To dequeue, we need to remove the head of the linked list. To do so, we will first store its data
in a variablebecause we will return it at last and then point head to its next element.
x = Q.head.data Q.head = Q.head.nextreturn x
We will execute the above codes when the queue is not empty. If it is, we will throw the "Queue
Underflow" error.

DEQUEUE(Q, n) if
IS_EMPTY(Q)
Error "Queue Underflow"else
x = Q.head.data Q.head =
Q.head.next
return x

Program

class Node():
def __init__(self, data):
self.data = data
self.next = None
class Queue():
def __init__(self):
self.head = None
self.tail = None
def traversal(q):
temp = q.head #temporary pointer to point to head
a = ''
while temp != None:
#iterating over queuea = a+str(temp.data)+'\t'
temp = temp.next
print(a)
def is_empty(q):
if q.head == None:
return True

51
return False
def enqueue(q, n):
if is_empty(q):
#emptyq.head = n
q.tail = n
else:
q.tail.next = n
q.tail = n
def dequeue(q):
if is_empty(q):
print("Queue Underflow")
return -1000
else:
x = q.head.data
temp = q.head
q.head = q.head.next
del temp
return x
if __name__ == '__main__':
q = Queue()
a = Node(10)
b = Node(20)
c = Node(30)
dequeue(q)
enqueue(q, a)
enqueue(q, b)
enqueue(q, c)
traversal(q)
dequeue(q)
traversal(q)

Priority Queues
A queue has FIFO (first-in-first-out) ordering where items are taken out or accessed on a
first-come-first-served basis. Examples of queues include a queue at a movie ticket stand, as
shown in the illustration above. But what is a priority queue?

A priority queue is an abstract data structure (a data structure defined by its behaviour) that is
like a normal queue but where each item has a special “key” to quantify its “priority”. For
example, if the movie cinema decides to serve loyal customers first, it will order them by their
loyalty (points or number of tickets purchased). In such a case, the queue for tickets will no longer
be first-come-first-served, but most-loyal- first-served. The customers will be the “items” of this
priority queue while the “priority” or “key” will be their loyalty.
52
A priority queue can be of two types:

1. Max Priority Queue: Which arranges the data as per descending order of their priority.
2. Min Priority Queue: Which arranges the data as per ascending order of their priority.

In a priority queue, following factors come into play:


1. In priority queue, data when inserted, is stored based on its priority.
2. When we remove a data from a priority queue(min), the data at the top, which will be the data
withleast priority, will get removed.
3. But, this way priority queue will not be following the basic principle of a queue, First in First
Out(FIFO). Yes, it won't! Because a priority queue is an advanced queue used when we have to
arrange and manipulate data as per the given priority.

Implementing Priority Queue


So now we will design our very own minimum priority queue using python list and object-
oriented concept.
Below are the algorithm steps:
1. Node: The Node class will be the element inserted in the priority queue. You can modifythe
Node class as per your requirements.
2. insert: To add a new data element (Node) in the priority queue.
o If the priority queue is empty, we will insert the element to it.
o If the priority queue is not empty, we will traverse the queue, comparing the priorities of
theexisting nodes with the new node, and we will add the new node before the node with
priority greater than the new node.
o If the new node has the highest priority, then we will add the new node at the end.
3. delete: To remove the element with least priority.
4. size: To check the size of the priority queue, in other words count the number of elements in
thequeue and return it.
5. show: To print all the priority queue elements.

Priority Queue Program


# class for Node with data and priorityclass Node:
def __init (self, info, priority):
self.info = info
self.priority = priority
# class for Priority queueclass PriorityQueue:
def __init (self):
self.queue = list()
# if you want you can set a maximum size for the queue
def insert(self, node):
# if queue is empty
if self.size() == 0:

53
# add the new node self.queue.append(node)
else:
# traverse the queue to find the right place for new node
for x in range(0, self.size()):
# if the priority of new node is greater
if node.priority >= self.queue[x].priority:
# if we have traversed the complete
queueif x == (self.size()-1):
# add new node at the end
self.queue.insert(x+1, node)
else:
continue
else:
self.queue.insert(x, node)
return True
def delete(self):
# remove the first node from the queue
return self.queue.pop(0)
def show(self):
for x in self.queue:
print str(x.info)+" - "+str(x.priority)
def size(self):
return len(self.queue)
pQueue = PriorityQueue()
node1 = Node("C", 3)
node2 = Node("B", 2)
node3 = Node("A", 1)
node4 = Node("Z", 26)
node5 = Node("Y", 25)
node6 = Node("L", 12)
pQueue.insert(node1)
pQueue.insert(node2)
pQueue.insert(node3)
pQueue.insert(node4)
pQueue.insert(node5)
pQueue.insert(node6)
pQueue.show()
print(" -------------- ")
pQueue.delete()
pQueue.show()

54
Cyber security

UNIT-3
Unit includes:

• Linear search
• Binary search
• Merge sort
• Bubble sort
• Selection sort
• Insertion sort
• Quick sort

Linear search:

Linear search is a method of finding elements within a list. It is also called a sequential
search. It is the simplest searching algorithm because it searches the desired element in a
sequential manner.

It compares each and every element with the value that we are searching for. If both are
matched, the element is found, and the algorithm returns the key's index position.

Let's understand the following steps to find the element key = 7 in the given list.

Step - 1: Start the search from the first element and Check key = 7 with each element
of list x.

Step - 2: If element is found, return the index position of the key.


Step - 3: If element is not found, return element is not present.

Linear Search Algorithm


There is list of n elements and key value to be searched.

Below is the linear search algorithm.

LinearSearch(list, key)
for each item in the list
if item == value
return its index position
return -1
EXAMPLE:
OUTPUT:
“Enter numbers,separated by whitespace: 10 5 67 56 78 45
“Enter searching element: 67
“Element Found”

Binary search:
A binary search is an algorithm to find a particular element in the list. Suppose we have
a list of thousand elements, and we need to get an index position of a particular
element. We can find the element's index position very fast using the binary search
algorithm.
The elements in the list must be sorted to apply the binary search algorithm. If
elements are not sorted then sort them first.

Concept of Binary Search:

In the binary search algorithm, we can find the element position using the following
methods.

o Recursive Method
o Iterative Method

The divide and conquer approach technique is followed by the recursive method. In
this method, a function is called itself again and again until it found an element in the
list.

A set of statements is repeated multiple times to find an element's index position in


the iterative method. The while loop is used for accomplish this task.

Binary search is more effective than the linear search because we don't need to search
each list index. The list must be sorted to achieve the binary search algorithm.

Let's have a step by step implementation of binary search.


We have a sorted list of elements, and we are looking for the index position of 45.

[12, 24, 32, 39, 45, 50, 54]

So, we are setting two pointers in our list. One pointer is used to denote the smaller
value called low and the second pointer is used to denote the highest value
called high.

Next, we calculate the value of the middle element in the array.

1. mid = (low+high)/2
2. Here, the low is 0 and the high is 7.
3. mid = (0+7)/2
4. mid = 3 (Integer)

Now, we will compare the searched element to the mid index value. In this case, 32 is
not equal to 45. So we need to do further comparison to find the element.

If the number we are searching equal to the mid. Then return mid otherwise move to
the further comparison.

The number to be search is greater than the middle number, we compare the n with
the middle element of the elements on the right side of mid and set low to

low = mid + 1.

Otherwise, compare the n with the middle element of the elements on the left side
of mid and set high to high = mid - 1.

Repeat until the number that we are searching for is found.


Example:

Output:
Enter Numbers,separated by whitespace: 5 15 20 25 30 35
Enter Searching Element:22
Element not found.

Merge Sort:
Merge sort algorithm works on the concept of divide and conquer. It divides the given
list in the two halves, calls itself for the two halves and then merges the two sorted
halves. We define the merge() function used to merging two halves.

The sub lists are divided again and again into halves until we get the only one element
each. 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.

Example:
Output:
Enter Numbers,separated by whitespace: 35 67 32 13 78
Before sorting: 35 67 32 13 78
After sorting: 13 32 35 67 78

Quick Sort:
Quicksort is the widely used sorting algorithm that makes n log n comparisons in
average case for sorting an array of n elements. It is a faster and highly efficient sorting
algorithm. This algorithm follows the divide and conquer approach.
Divide and conquer is a technique of breaking down the algorithms into subproblems,
then solving the subproblems, and combining the results back together to solve the
original problem.

Quicksort picks an element as pivot, and then it partitions the given array around the
picked pivot element. In quick sort, a large array is divided into two arrays in which
one holds values that are smaller than the specified value (Pivot), and another array
holds the values that are greater than the pivot.

After that, left and right sub-arrays are also partitioned using the same approach. It
will continue until the single element remains in the sub-array.
Choosing the pivot:

Picking a good pivot is necessary for the fast implementation of quicksort. However, it
is typical to determine a good pivot. Some of the ways of choosing a pivot are as
follows -

o Pivot can be random, i.e. select the random pivot from the given array.
o Pivot can either be the rightmost element of the leftmost element of the given array.
o Select median as the pivot element.

Example:

Out put:
Enter Numbers separated by whitespace: 45 56 3 51 14 44
Unsorted Array
[45,56,3,51,14,44]
Sorted Array in Ascending Order: [3,14,44,45,51,56]
Bubble sort:
Bubble sort is a sorting algorithm that compares two adjacent elements and swaps them
until they are in the intended order. Just like the movement of air bubbles in the water
that rise up to the surface, each element of the array move to the end in each iteration.
Therefore, it is called a bubble sort.
Bubble short is majorly used where :
o complexity does not matter
o simple and shortcode is preferred.

Example:

Output:

Enter Numbers separated by whitespace: 54 32 96 12 2 126

Before Swapping: [54, 32, 96, 12, 2, 126]

After Swapping:[2, 12, 32, 54, 96, 126]

Selection Sort:

In this algorithm, we select the smallest element from an unsorted array in each pass and swap
with the beginning of the unsorted array. This process will continue until all the elements are
placed at right place. It is simple and an in-place comparison sorting algorithm.

Working of Selection Sort

The following are the steps to explain the working of the Selection sort in Python.

Let's take an unsorted array to apply the selection sort algorithm.

[30, 10, 12, 8, 15, 1]

Step - 1: Get the length of the array.

length = len(array) → 6
Step - 2: First, we set the first element as minimum element.

Step - 3: Now compare the minimum with the second element. If the second element is
smaller than the first, we assign it as a minimum.

Again we compare the second element to the third and if the third element is smaller than
second, assign it as minimum. This process goes on until we find the last element.

Step - 4: After each iteration, minimum element is swapped in front of the unsorted array.

Step - 5: The second to third steps are repeated until we get the sorted array.

Example:

Output:

Enter Numbers,separated by whitespace: 51 45 8 14 28 33

Before swapping: [51,45,8,14,28,33]

After swapping: [8, 14, 28, 33, 45, 51]

Insertion sort:

Insertion sort is a simple sorting algorithm that builds the final sorted array (or list) one item
at a time by comparisons .

To sort the array using insertion sort below is the algorithm of insertion sort.

o Spilt a list in two parts - sorted and unsorted.


o Iterate from arr[1] to arr[n] over the given array.
o Compare the current element to the next element.
o If the current element is smaller than the next element, compare to the element
before, Move to the greater elements one position up to make space for the swapped
element.

Example:
UNIT-4
Trees:
The tree is another data structure that allows the data to reside in a
hierarchical position. Each tree has a node, and each node has a leaf also called
a child.
• Root → The topmost node of the hierarchy is called the root of the tree.

• Child → Nodes next in the hierarchy are the children of the previous node.
• Parent → The node just previous to the current node is the parent of the
current node.
• Siblings → Nodes with the same parent are called siblings.
. • Internal Nodes → Nodes with at least one child are internal nodes.
• External Nodes/Leaves → Nodes which don't have any child are called leaves
of a tree.
• Edge → The link between two nodes is called an edge.

• Level → The root of a tree is at level 0 and the nodes whose parent is root are
at level 1 and so on.
→ Node Degree: It is the maximum number of children a node has.

o Tree Degree → Tree degree is the maximum of the node degrees. So, the
tree degree in the abovepicture is 3.
Height → The height of a node is the number of nodes (excluding the node) on
the longest pathfrom the node to a leaf

Types of binary trees:

There are four types of Binary tree:


o Full/ proper/ strict Binary tree
o Complete Binary tree
o Perfect Binary tree
o Balanced Binary tree
o Skewed binary tree

1. Full/ proper/ strict Binary tree

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.

Properties of Full Binary Tree

o The number of leaf nodes is equal to the number of internal nodes plus 1. In
the above example, the number of internal nodes is 5; therefore, the number of
leaf nodes is equal to 6.
o The maximum number of nodes is the same as the number of nodes in the
binary tree, i.e., 2h+1 -1.
o The minimum number of nodes in the full binary tree is 2*h-1.
o The minimum height of the full binary tree is log2(n+1) - 1.
o The maximum height of the full binary tree can be computed as:

n= 2*h - 1

n+1 = 2*h

h = n+1/2

Complete Binary Tree

The complete binary tree is a tree in which all the nodes are completely filled except
the last level. In the last level, all the nodes must be as left as possible. In a complete
binary tree, the nodes should be added from the left.
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.

Properties of Complete Binary Tree

o The maximum number of nodes in complete binary tree is 2h+1 - 1.


o The minimum number of nodes in complete binary tree is 2h.
o The minimum height of a complete binary tree is log2(n+1) - 1.
o The maximum height of a complete binary tree is

Perfect Binary Tree:

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.

Skewed binary tree:

All the nodes except one node has one and only one child.

The remaining node has no child.


Avl trees or balanced binary tree:

AVL Tree can be defined as height balanced binary search tree in which each node is
associated with a balance factor which is calculated by subtracting the height of its
right sub-tree from that of its left sub-tree.

If balance factor of any node is 0, it means that the left sub-tree and right sub-tree
contain equal height.

If balance factor of any node is -1, it means that the left sub-tree is one level lower
than the right sub-tree.

An AVL tree is given in the following figure. We can see that, balance factor associated
with each node is in between -1 and +1. therefore, it is an example of AVL tree.

1. L L rotation: Inserted node is in the left subtree of left subtree of A


2. R R rotation : Inserted node is in the right subtree of right subtree of A
3. L R rotation : Inserted node is in the right subtree of left subtree of A
4. R L rotation : Inserted node is in the left subtree of right subtree of A

RR Rotation:

LL Rotation

LR Rotation

State Action

A node B has been inserted into the right subtree of A the


left subtree of C, because of which C has become
an unbalanced node having balance factor 2.
This case is L R rotation where: Inserted node is in the
right subtree of left subtree of C
As LR rotation = RR + LL rotation, hence RR (anticlockwise) on
subtree rooted at A is performed first.
By doing RR rotation, node A, has become the left subtree of B.

After performing RR rotation, node C is still unbalanced,


i.e., having balance factor 2, as inserted node A is in the left
of left of C

Now we perform LL clockwise rotation on full tree, i.e.


on node C. node C has now become the right subtree
of node B, A is left subtree of B

Balance factor of each node is now either -1, 0, or 1, i.e. BST


is balanced now.

RL Rotation

State Action

A node B has been inserted into the left subtree of C


the right subtree of A, because of which A has become an
unbalanced node having balance factor - 2. This case is RL rotation
where: Inserted node is in the left subtree of right subtree of A
As RL rotation = LL rotation + RR rotation,
hence, LL (clockwise) on subtree rooted at C
is performed first. By doing RR rotation, node C
has become the right subtree of B.

After performing LL rotation, node A is still unbalanced,


i.e. having balance factor -2, which is because of the right-subtree
of the right-subtree node A.

Now we perform RR rotation (anticlockwise rotation) on full tree,


i.e. on node A. node C has now become the right subtree of node B
, and node A has become the left subtree of B.

Balance factor of each node is now either -1, 0, or 1, i.e., BST is


balanced now.

Tree traversal (Inorder, Preorder an Postorder):


o Preorder traversal
o Inorder traversal
o Postorder traversal

Preorder:

This technique follows the 'root left right' policy.


The applications of preorder traversal include -

o It is used to create a copy of the tree.


o It can also be used to get the prefix expression of an expression tree.

Postorder traversal:
This technique follows the 'left-right root' policy.

The applications of postorder traversal include –

o It is used to delete the tree.


o It can also be used to get the postfix expression of an expression tree.

Inorder traversal
This technique follows the 'left root right' policy.

The applications of Inorder traversal includes -

o It is used to get the BST nodes in increasing order.


o It can also be used to get the prefix expression of an expression tree.
Example for Tree traversal:
Output:
BINARY SEARCH TREE:
A binary search tree follows some order to arrange the elements.
In a Binary search tree, the value of left node must be smaller
than the parent node, and the value of right node must be
greater than the parent node. This rule is applied recursively to
the left and right subtrees of the root.

Advantages of Binary search tree

o Searching an element in the Binary search tree is easy as we always have a hint
that which subtree has the desired element.
o As compared to array and linked lists, insertion and deletion operations are
faster in BST.

Algorithm to search an element in Binary search tree

1. Search (root, item)


2. Step 1 - if (item = root → data) or (root = NULL)
3. return root
4. else if (item < root → data)
5. return Search(root → left, item)
6. else
7. return Search(root → right, item)
8. END if
9. Step 2 - END

Deletion in Binary Search tree


In a binary search tree, we must delete a node from the tree by keeping in mind that
the property of BST is not violated. To delete a node from BST, there are three possible
situations occur -

o The node to be deleted is the leaf node, or,


o The node to be deleted has only one child, and,
o The node to be deleted has two children

Insertion in Binary Search tree


A new key in BST is always inserted at the leaf. To insert an element in BST, we have to
start searching from the root node; if the node to be inserted is less than the root
node, then search for an empty location in the left subtree. Else, search for the empty
location in the right subtree and insert the data. Insert in BST is similar to searching, as
we always have to maintain the rule that the left subtree is smaller than the root, and
right subtree is larger than the root.
UNIT-5

GRAPHS

Graphs-Introduction:

A graph is an advanced data structure that is used to organize items in an


interconnected network. Each item in a graph is known as a node (or vertex) and
these nodes are connected by edges.

A Graph is a non-linear data structure consisting of vertices and edges. The


vertices are sometimes also referred to as nodes and 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(E, V).

Components of a Graph:

 Vertices: Vertices are the fundamental units of the graph. Sometimes,


vertices are also known as vertex or nodes. Every node/vertex can be
labeled or unlabelled.

 Edges: Edges are drawn or used to connect two nodes of the graph. It can
be ordered pair of nodes in a directed graph. Edges can connect any two
nodes in any possible way. There are no rules. Sometimes, edges are also
known as arcs. Every edge can be labeled/unlabelled.

In the figure below, we have a simple graph where there are five nodes in total
and seven edges.
The nodes in any graph can be referred to as entities and the edges that connect
different nodes define the relationships between these entities. In the above
graph we have a set of nodes {V} = {A, B, C, D,E} and a set of edges, {E} = {A-B, A-
D, B-C, B-D, C-D, D-E} respectively.

Commonly Used Graph Terminologies :

 Path - A sequence of alternating nodes and edges such that each of the
successive nodes areconnected by the edge.

 Cycle - A path where the starting and the ending node is the same.

 Simple Path - A path where we do not encounter a vertex again.

 Bridge - An edge whose removal will simply make the graph disconnected.

 Forest - A forest is a graph without cycles.

 Tree - A connected graph that doesn't have any cycles.

 Degree - The degree in a graph is the number of edges that are incident on a
particular node.

 Neighbour - We say vertex "A" and "B" are neighbours if there exists an edge
between them.

 Adjacency - Vertices are said to be adjacent to one another if there is an edge


connecting them.

 Self-loop - An edge is called a self-loop if its two endpoints coincide with each
other.

 Undirected Edge - It is a bidirectional edge.

 Directed Edge - It is a unidirectional edge.

 Weighted Edge - An edge with value (cost) on it.


Types of Graphs:

1 . Null Graphs :

A graph is said to be null if there are no edges in that graph.

A pictorial representation of the null graph is given below:

2 . Undirected Graphs :

In an undirected graph, the edges have no path or direction. If there is a path


from vertex X to vertex Y, then there is a path from vertex Y to vertex X. Edge (X,
Y) represents the edge connecting vertex X to vertex Y.

That is, edge (X, Y) == edge (Y, X).

A pictorial representation of another undirected graph is given below:


3 . Directed Graphs :

In a directed graph or digraph, the edges have an orientation. If there is a path


from vertex X to vertex Y, then there isn’t necessarily a path from vertex Y to
vertex X.

That is, edge (X, Y) != edge (Y, X).

A pictorial representation of the graph is given below:

4. Cyclic Graphs :

A graph that contains at least one node that traverses back to itself is known as a
cyclic graph. In simple words, a graph should have at least one cycle formation for
it to be called a cyclic graph.

A pictorial representation of a cyclic graph is given below:


5 . Acyclic Graphs :

A graph where there's no way we can start from one node and can traverse back
to the same one, or simply doesn't have a single cycle is known as an acyclic
graph.

A pictorial representation of an acyclic graph is given below:

6 . Weighted Graphs :

When the edge in a graph has some weight associated with it, we call that graph
as a weighted graph. The weight is generally a number that could mean anything,
totally dependent on the relationship between the nodes of that graph.

A pictorial representation of the weighted graph is given below :


7 . Unweighted Graphs :

An unweighted graph does not have a value associated with every edge.

A pictorial representation of the unweighted graph is given below :

8 . Connected Graphs :

A graph where we have a path between every two nodes of the graph is known as
a connected graph. A path here means that we are able to traverse from a node
"A" to say any node "B". In simple terms, we can say that if we start from one
node of the graph we will always be able to traverse to all the other nodes of the
graph from that node, hence the connectivity.

A pictorial representation of the connected graph is given below:


9 . Disconnected Graphs :

A graph that is not connected is simply known as a disconnected graph. In a


disconnected graph, we will not be able to find a path from between every two
nodes of the graph.

A pictorial representation of the disconnected graph is given below:

10 . Complete Graphs :

A simple graph with n vertices is called a complete graph if the degree of each
vertex is n-1, that is, one vertex is attached with n-1 edges or the rest of the
vertices in the graph. A complete graph is also called Full Graph.

A pictorial representation of the complete graph is given below:


11 . Multi Graph :

A graph is said to be a multigraph if there exist two or more than two edges
between any pair of nodes in the graph.

Any graph which contains some parallel edges but doesn’t contain any self-loop is
called a multigraph.

A pictorial representation of the multigraph is given below:

Breadth First Search (BFS) :

BFS is an algorithm that is used to graph data or searching tree or traversing


structures. The algorithm efficiently visits and marks all the key nodes in a graph
in anaccurate breadthwise fashion.

This algorithm selects a single node (initial or source point) in a graph and then
visits all the nodes adjacent to the selected node. Once the algorithm visits and
marks the starting node, then it moves towards the nearest unvisited nodes and
analyses them.

Once visited, all nodes are marked. These iterations continue until all the nodes of
the graph have been successfully visited and marked. The full form of BFS is the
Breadth-first search.
Steps in Breadth First Search:

1. Start by putting any one of the graph’s vertices at the back of a queue.

2. Take the front item of the queue and add it to the visited list.

3. Create a list of that vertex’s adjacent nodes. Add the ones which aren’t in the
visited list to the back of the queue.

4. Keep repeating steps 2 and 3 until the queue is empty.

5. You have a graph of seven numbers ranging from 0 – 6.

6. 0 or zero has been marked as a root node

7. 0 is visited, marked, and inserted into the queue data structure

8. Remaining 0 adjacent and unvisited nodes are visited, marked, and inserted
into the queue.

9. Traversing iterations are repeated until all nodes are visited.

Example:

Step1: Initially queue and visited arrays are empty.

Queue and visited arrays are empty initially.


Step2: Push node 0 into queue and mark it visited.

Push node 0 into queue and mark it visited.

Step 3: Remove node 0 from the front of queue and visit the unvisited neighbours
and push them into queue.

Remove node 0 from the front of queue and visited the unvisited neighbours and push into queue.

Step 4: Remove node 1 from the front of queue and visit the unvisited neighbours
and push them into queue.
Remove node 1 from the front of queue and visited the unvisited neighbours and push

Step 5: Remove node 2 from the front of queue and visit the unvisited neighbours
and push them into queue.

Remove node 2 from the front of queue and visit the unvisited neighbours and push them into queue.

Step 6: Remove node 3 from the front of queue and visit the unvisited
neighbours and push them into queue.

As we can see that every neighbours of node 3 is visited, so move to the next
node that are in the front of the queue.
Remove node 3 from the front of queue and visit the unvisited neighbours and push them into queue.

Steps 7: Remove node 4 from the front of queue and and visit the unvisited
neighbours and push them into queue.
As we can see that every neighbours of node 4 are visited, so move to the next
node that is in the front of the queue.

Remove node 4 from the front of queue and and visit the unvisited neighbours and push them into
queue.

Now, Queue becomes empty, So, terminate these process of iteration.


Code:
# Python3 Program to print BFS traversal

# from a given source vertex.

class Graph:

def __init__(self,gdict=None):

if gdict is None:

gdict={}

self.gdict=gdict

def addEdge(self,vertex,edge):

self.gdict[vertex].append(edge)

def bfs(self,vertex):

visited=[vertex]

queue=[vertex]

while queue:

devertex=queue.pop(0)

print(devertex)

for adjacentvertex in self.gdict[devertex]:

if adjacentvertex not in visited:

visited.append(adjacentvertex)

queue.append(adjacentvertex)
customdict={"a":["b","c"],

"b":["a","d","g"],

"c":["a","d","e"],

"d":["b","c","f"],

"e":["c","f"],

"f":["d","e","g"],

"g":["b","f"]}

graph=Graph(customdict)

graph.addEdge("a","f")

print("Breadth First Search")

graph.bfs("a")

OUTPUT:
Breadth First Search

e
Depth First Search (DFS) :

DFS is an algorithm for finding or traversing graphs or trees in depth-ward


direction. The execution of the algorithm begins at the root node and explores
each branch before backtracking. It uses a stack data structure to remember, to
get the subsequent vertex, and to start a search, whenever a dead-end appears in
any iteration. The full form of DFS is Depth-first search.

Steps in Depth First Search:

1. Start by putting any one of the graph’s vertices on top of a stack.

2. Take the top item of the stack and add it to the visited list.

3. Create a list of that vertex’s adjacent nodes. Add the ones which aren’t in the
visited

list to the top of the stack.

4. Keep repeating steps 2 and 3 until the stack is empty.

Example of DFS :

In the following example of DFS, we have used an undirected graph having


5 vertices.
Step 1)

We have started from vertex 0. The algorithm begins by putting it in the visited
list and simultaneously putting all its adjacent vertices in the data structure called
stack.

Step 2)

You will visit the element, which is at the top of the stack, for example, 1 and go
to its adjacent nodes. It is because 0 has already been visited. Therefore, we visit
vertex 2.
Step 3)

Vertex 2 has an unvisited nearby vertex in 4. Therefore, we add that in the stack
and visit it.

Step 4)

Finally, we will visit the last vertex 3, it doesn't have any unvisited adjoining
nodes. We have completed the traversal of the graph using DFS algorithm.
CODE:
class Graph:

def __init__(self,gdict=None):

if gdict is None:

gdict={}

self.gdict=gdict

def addEdge(self,vertex,edge):

self.gdict[vertex].append(edge)

def dfs(self,vertex):

visited=[vertex]

stack=[vertex]

while stack:

popvertex=stack.pop()

print(popvertex)

for adjacentvertex in self.gdict[popvertex]:

if adjacentvertex not in visited:


visited.append(adjacentvertex)

stack.append(adjacentvertex)

customdict={"a":["b","c"],

"b":["a","d","g"],

"c":["a","d","e"],

"d":["b","c","f"],

"e":["c","f"],

"f":["d","e","g"],

"g":["b","f"]}

graph=Graph(customdict)

graph.addEdge("a","f")

print(graph.gdict)

print("Depth First Search")

graph.dfs("a")

OUTPUT:
{'a': ['b', 'c', 'f'], 'b': ['a', 'd', 'g'], 'c': ['a', 'd', 'e'], 'd': ['b', 'c', 'f'], 'e': ['c', 'f'], 'f': ['d',
'e', 'g'], 'g': ['b', 'f']}

Depth First Search

e
d

Directed Acyclic Graph :


A directed acyclic graph (DAG) is a graph which doesn’t contain a cycle and has
directed edges. We are given a DAG, we need to clone it, i.e., create another
graph that has copy of its vertices and edges connecting them.

Example:

Input :
0 - - - > 1 - - - -> 4
| / \ ^
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
v v v |
2 - - - - - - - - -> 3

Output : Printing the output of the cloned graph gives:


0-1
1-2
2-3
3-4
1-3
1-4
0-2
To clone a DAG without storing the graph itself within a hash (or dictionary in
Python). To clone, it we basically do a depth-first traversal of the nodes, taking
original node’s value and initializing new neighboring nodes with the same
value, recursively doing, till the original graph is fully traversed. Below is the
recursive approach to cloning a DAG (in Python). We make use of dynamic lists
in Python, append operation to this list happens in constant time, hence, fast
and efficient initialization of the graph.

Topological Sorting :

Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering of


vertices such that for every directed edge uv, vertex u comes before v in the
ordering. Topological Sorting for a graph is not possible if the graph is not a DAG.
For example, a topological sorting of the following graph is “5 4 2 3 1 0”. There
can be more than one topological sorting for a graph. For example, another
topological sorting of the following graph is “4 5 2 3 1 0”. The first vertex in
topological sorting is always a vertex with in-degree as 0 (a vertex with no in-
coming edges).
Program :
#Python program to print topological sorting of a DAG

from collections import defaultdict

#Class to represent a graph

class Graph:

def __init__(self,vertices):

self.graph = defaultdict(list) #dictionary containing adjacency List

self.V = vertices #No. of vertices

# function to add an edge to graph

def addEdge(self,u,v):

self.graph[u].append(v)

# A recursive function used by topologicalSort

def topologicalSortUtil(self,v,visited,stack):

# Mark the current node as visited.

visited[v] = True

# Recur for all the vertices adjacent to this vertex

for i in self.graph[v]:
if visited[i] == False:

self.topologicalSortUtil(i,visited,stack)

# Push current vertex to stack which stores result

stack.insert(0,v)

# The function to do Topological Sort. It uses recursive

# topologicalSortUtil()

def topologicalSort(self):

# Mark all the vertices as not visited

visited = [False]*self.V

stack =[]

# Call the recursive helper function to store Topological

# Sort starting from all vertices one by one

for i in range(self.V):

if visited[i] == False:

self.topologicalSortUtil(i,visited,stack)

# Print contents of stack

print (stack)
g= Graph(6)

g.addEdge(5, 2);

g.addEdge(5, 0);

g.addEdge(4, 0);

g.addEdge(4, 1);

g.addEdge(2, 3);

g.addEdge(3, 1);

print ("Following is a Topological Sort of the given graph")

g.topologicalSort()

Output:

Following is a Topological Sort of the given graph

[5, 4, 2, 3, 1, 0]

Dijkstra's Algorithm

Dijkstra algorithm is a single-source shortest path algorithm.Computes the


shortest path from one particular source node to all other remaining nodes of the
graph. works only for connected graphs and for those graphs that do not contain
any negative weight edge.Only provides the value or cost of the shortest
paths.Works for directed as well as undirected graphs.

Working of Dijkstra's algorithm:


First, we have to consider any vertex as a source vertex. Suppose we consider
vertex 0 as a source vertex.

Here we assume that 0 as a source vertex, and distance to all the other vertices is
infinity. Initially, we do not know the distances. First, we will find out the vertices
which are directly connected to the vertex 0. As we can observe in the above
graph that two vertices are directly connected to vertex 0.

Let's assume that the vertex 0 is represented by 'x' and the vertex 1 is
represented by 'y'. The distance between the vertices can be calculated by using
the below formula:

d(x, y) = d(x) + c(x, y) < d(y)


= (0 + 4) < ∞
=4<∞
Since 4<∞ so we will update d(v) from ∞ to 4.
Therefore, we come to the conclusion that the formula for calculating the distance
between the vertices:
{if( d(u) + c(u, v) < d(v))
d(v) = d(u) +c(u, v) }
Now we consider vertex 0 same as 'x' and vertex 4 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (0 + 8) < ∞
=8<∞
Therefore, the value of d(y) is 8. We replace the infinity value of vertices
1 and 4 with the values 4 and 8 respectively. Now, we have found the
shortest path from the vertex 0 to 1 and 0 to 4. Therefore, vertex 0 is
selected. Now, we will compare all the vertices except the vertex 0. Since
vertex 1 has the lowest value, i.e., 4; therefore, vertex 1 is selected.
Since vertex 1 is selected, so we consider the path from 1 to 2, and 1 to
4. We will not consider the path from 1 to 0 as the vertex 0 is already
selected.
First, we calculate the distance between the vertex 1 and 2. Consider the
vertex 1 as 'x', and the vertex 2 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (4 + 8) < ∞
= 12 < ∞
Since 12<∞ so we will update d(2) from ∞ to 12.
Now, we calculate the distance between the vertex 1 and vertex 4. Consider the
vertex 1 as 'x' and the vertex 4 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (4 + 11) < 8
= 15 < 8
Since 15 is not less than 8, we will not update the value d(4) from 8 to 12.
Till now, two nodes have been selected, i.e., 0 and 1. Now we have to compare
the nodes except the node 0 and 1. The node 4 has the minimum distance, i.e.,
8. Therefore, vertex 4 is selected.
Since vertex 4 is selected, so we will consider all the direct paths from the
vertex 4. The direct paths from vertex 4 are 4 to 0, 4 to 1, 4 to 8, and 4 to 5.
Since the vertices 0 and 1 have already been selected so we will not consider
the vertices 0 and 1. We will consider only two vertices, i.e., 8 and 5.
First, we consider the vertex 8. First, we calculate the distance between the
vertex 4 and 8. Consider the vertex 4 as 'x', and the vertex 8 as 'y'.

d(x, y) = d(x) + c(x, y) < d(y)


= (8 + 7) < ∞
= 15 < ∞
Since 15 is less than the infinity so we update d(8) from infinity to 15.
Now, we consider the vertex 5. First, we calculate the distance between the vertex 4
and 5. Consider the vertex 4 as 'x', and the vertex 5 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (8 + 1) < ∞
=9<∞
Since 5 is less than the infinity, we update d(5) from infinity to 9.
Till now, three nodes have been selected, i.e., 0, 1, and 4. Now we have to compare
the nodes except the nodes 0, 1 and 4. The node 5 has the minimum value, i.e., 9.
Therefore, vertex 5 is selected.
Since the vertex 5 is selected, so we will consider all the direct paths from vertex 5.
The direct paths from vertex 5 are 5 to 8, and 5 to 6.
First, we consider the vertex 8. First, we calculate the distance between the vertex 5
and 8. Consider the vertex 5 as 'x', and the vertex 8 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (9 + 15) < 15
= 24 < 15
Since 24 is not less than 15 so we will not update the value d(8) from 15 to 24.
Now, we consider the vertex 6. First, we calculate the distance between the vertex 5
and 6. Consider the vertex 5 as 'x', and the vertex 6 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (9 + 2) < ∞</p>
= 11 < ∞
Since 11 is less than infinity, we update d(6) from infinity to 11.
Till now, nodes 0, 1, 4 and 5 have been selected. We will compare the nodes except
the selected nodes. The node 6 has the lowest value as compared to other nodes.
Therefore, vertex 6 is selected.
Since vertex 6 is selected, we consider all the direct paths from vertex 6. The direct
paths from vertex 6 are 6 to 2, 6 to 3, and 6 to 7.
First, we consider the vertex 2. Consider the vertex 6 as 'x', and the vertex 2 as 'y'.

d(x, y) = d(x) + c(x, y) < d(y)


= (11 + 4) < 12
= 15 < 12
Since 15 is not less than 12, we will not update d(2) from 12 to 15
Now we consider the vertex 3. Consider the vertex 6 as 'x', and the vertex 3 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (11 + 14) < ∞
= 25 < ∞
Since 25 is less than ∞, so we will update d(3) from ∞ to 25.
Now we consider the vertex 7. Consider the vertex 6 as 'x', and the vertex 7 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (11 + 10) < ∞
= 22 < ∞
Since 22 is less than ∞ so, we will update d(7) from ∞ to 22.
Till now, nodes 0, 1, 4, 5, and 6 have been selected. Now we have to compare all the
unvisited nodes, i.e., 2, 3, 7, and 8. Since node 2 has the minimum value, i.e., 12
among all the other unvisited nodes. Therefore, node 2 is selected.
Since node 2 is selected, so we consider all the direct paths from node 2. The direct
paths from node 2 are 2 to 8, 2 to 6, and 2 to 3.
First, we consider the vertex 8. Consider the vertex 2 as 'x' and 8 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (12 + 2) < 15
= 14 < 15
Since 14 is less than 15, we will update d(8) from 15 to 14.
Now, we consider the vertex 6. Consider the vertex 2 as 'x' and 6 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (12 + 4) < 11
= 16 < 11
Since 16 is not less than 11 so we will not update d(6) from 11 to 16.
Now, we consider the vertex 3. Consider the vertex 2 as 'x' and 3 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (12 + 7) < 25
= 19 < 25
Since 19 is less than 25, we will update d(3) from 25 to 19.
Till now, nodes 0, 1, 2, 4, 5, and 6 have been selected. We compare all the unvisited
nodes, i.e., 3, 7, and 8. Among nodes 3, 7, and 8, node 8 has the minimum value. The
nodes which are directly connected to node 8 are 2, 4, and 5. Since all the directly
connected nodes are selected so we will not consider any node for the updation.
The unvisited nodes are 3 and 7. Among the nodes 3 and 7, node 3 has the minimum
value, i.e., 19. Therefore, the node 3 is selected. The nodes which are directly
connected to the node 3 are 2, 6, and 7. Since the nodes 2 and 6 have been selected
so we will consider these two nodes.
Now, we consider the vertex 7. Consider the vertex 3 as 'x' and 7 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (19 + 9) < 21
= 28 < 21
Since 28 is not less than 21, so we will not update d(7) from 28 to 21.

program for dijkstra’s algorithm:

from collections import defaultdict

class Graph:

def __init__(self):

self.nodes = set()

self.edges = defaultdict(list)

self.distances = {}

def addNode(self,value):

self.nodes.add(value)

def addEdge(self, fromNode, toNode, distance):

self.edges[fromNode].append(toNode)

self.distances[(fromNode, toNode)] = distance

def dijkstra(graph, initial):

visited = {initial : 0}

path = defaultdict(list)
nodes = set(graph.nodes)

while nodes:

minNode = None

for node in nodes:

if node in visited:

if minNode is None:

minNode = node

elif visited[node] < visited[minNode]:

minNode = node

if minNode is None:

break

nodes.remove(minNode)

currentWeight = visited[minNode]

for edge in graph.edges[minNode]:

weight = currentWeight + graph.distances[(minNode, edge)]

if edge not in visited or weight < visited[edge]:

visited[edge] = weight

path[edge].append(minNode)

return visited, path


customGraph = Graph()

customGraph.addNode("A")

customGraph.addNode("B")

customGraph.addNode("C")

customGraph.addNode("D")

customGraph.addNode("E")

customGraph.addNode("F")

customGraph.addNode("G")

customGraph.addEdge("A", "B", 2)

customGraph.addEdge("A", "C", 5)

customGraph.addEdge("B", "C", 6)

customGraph.addEdge("B", "D", 1)

customGraph.addEdge("B", "E", 3)

customGraph.addEdge("C", "F", 8)

customGraph.addEdge("D", "E", 4)

customGraph.addEdge("E", "G", 9)

customGraph.addEdge("F", "G", 7)

print(dijkstra(customGraph, "A"))

# See change the distance from d to e to 1 and from b to e to 6.

# then to get to e from a ,

# shortest path should be a b d e

# but your code is giving a b e


OUTPUT:
({'A': 0, 'B': 2, 'C': 5, 'D': 3, 'E': 5, 'G': 14, 'F': 13}, defaultdict(<class 'list'>, {'B': ['A'],
'C': ['A'], 'D': ['B'], 'E': ['B'], 'G': ['E'], 'F': ['C']}))

Bellman Ford Algorithm

Bellman ford algorithm is a single-source shortest path algorithm. This algorithm


is used to find the shortest distance from the single vertex to all the other vertices
of a weighted graph. There are various other algorithms used to find the shortest
path like Dijkstra algorithm, etc. If the weighted graph contains the negative
weight values, then the Dijkstra algorithm does not confirm whether it produces
the correct answer or not. In contrast to Dijkstra algorithm, bellman ford
algorithm guarantees the correct answer even if the weighted graph contains the
negative weight values.

Rule of this algorithm:

We will go on relaxing all the edges (n - 1) times where, n = number of vertices .

Program for bellman ford algorithm:


class Graph:

def __init__(self, vertices):

self.V = vertices

self.graph = []
self.nodes = []

def add_edge(self, s, d, w):

self.graph.append([s, d, w])

def addNode(self,value):

self.nodes.append(value)

def print_solution(self, dist):

print("Vertex Distance from Source")

for key, value in dist.items():

print(' ' + key, ' : ', value)

def bellmanFord(self, src):

dist = {i : float("Inf") for i in self.nodes}

dist[src] = 0

for _ in range(self.V-1):

for s, d, w in self.graph:

if dist[s] != float("Inf") and dist[s] + w < dist[d]:

dist[d] = dist[s] + w

for s, d, w in self.graph:

if dist[s] != float("Inf") and dist[s] + w < dist[d]:


print("Graph contains negative cycle")

return

self.print_solution(dist)

g = Graph(5)

g.addNode("A")

g.addNode("B")

g.addNode("C")

g.addNode("D")

g.addNode("E")

g.add_edge("A", "C", 6)

g.add_edge("A", "D", 6)

g.add_edge("B", "A", 3)

g.add_edge("C", "D", 1)

g.add_edge("D", "C", 2)

g.add_edge("D", "B", 1)

g.add_edge("E", "B", 4)

g.add_edge("E", "D", 2)

g.bellmanFord("E")

OUTPUT:
Vertex Distance from Source

A : 6

B : 3
C : 4

D : 2

E : 0

Floyd Warshall Algorithm

The Floyd Warshall Algorithm is for solving all pairs of shortest-path problems.
The problem is to find the shortest distances between every pair of vertices in a
given edge-weighted directed Graph.

 Initialize the solution matrix same as the input graph matrix as a first step.

 Then update the solution matrix by considering all vertices as an


intermediate vertex.

 The idea is to one by one pick all vertices and updates all shortest paths
which include the picked vertex as an intermediate vertex in the shortest
path.

 When we pick vertex number k as an intermediate vertex, we already have


considered vertices {0, 1, 2, .. k-1} as intermediate vertices.

 For every pair (i, j) of the source and destination vertices respectively, there
are two possible cases.

 k is not an intermediate vertex in shortest path from i to j. We keep


the value of dist[i][j] as it is.

 k is an intermediate vertex in shortest path from i to j. We update the


value of dist[i][j] as dist[i][k] + dist[k][j] if dist[i][j] > dist[i][k] +
dist[k][j]

Program for Floyd Warshall Algorithm:


INF = 9999
# Printing the solution
def printSolution(nV, distance):
for i in range(nV):
for j in range(nV):
if(distance[i][j] == INF):
print("INF", end=" ")
else:
print(distance[i][j], end=" ")
print(" ")

def floydWarshall(nV, G):


distance = G
for k in range(nV):
for i in range(nV):
for j in range(nV):
distance[i][j] = min(distance[i][j], distance[i][k]+distance[k][j])

printSolution(nV, distance)

G = [[0, 8, INF,1],
[INF, 0, 1,INF],
[4, INF, 0,INF],
[INF, 2, 9,1]
]

floydWarshall(4, G)

OUTPUT:
0 3 4 1

5 0 1 6

4 7 0 5

7 2 3 1
Spanning Tree

A Spanning tree is a subset to a connected graph G, where all the edges are
connected, i.e, one can traverse to any edge from a particular edge with or
without intermediates. Also, a spanning tree must not have any cycle in it. Thus
we can say that if there are N vertices in a connected graph then the no. of edges
that a spanning tree may have is N-1.

Minimum Spanning Tree


A minimum spanning tree (MST) or minimum weight spanning tree for a
weighted, connected, undirected graph is a spanning tree with a weight less
than or equal to the weight of every other spanning tree. The weight of a
spanning tree is the sum of weights given to each edge of the spanning tree.

To find MST we have two algorithms.They are:

1. Kruskal’s Algorithm
2. Prim’s Algorithm

Kruskal’s Minimum Spanning Tree Algorithm


An algorithm to construct a Minimum Spanning Tree for a connected weighted
graph. It is a Greedy Algorithm. The Greedy Choice is to put the smallest weight
edge that does not because a cycle in the MST constructed so far.

Below are the steps for finding MST using Kruskal’s algorithm:

1. Sort all the edges in non-decreasing order of their weight.

2. Pick the smallest edge. Check if it forms a cycle with the spanning tree
formed so far. If the cycle is not formed, include this edge. Else, discard it.
3. Repeat step#2 until there are (V-1) edges in the spanning tree.

Prim's Algorithm

Prim's Algorithm is a greedy algorithm that is used to find the minimum spanning
tree from a graph. Prim's algorithm finds the subset of edges that includes every
vertex of the graph such that the sum of the weights of the edges can be
minimized.

Prim's algorithm starts with the single node and explores all the adjacent nodes
with all the connecting edges at every step. The edges with the minimal weights
causing no cycles in the graph got selected.

The steps to implement the prim's algorithm are given as follows –

o First, we have to initialize an MST with the randomly chosen vertex.

o Now, we have to find all the edges that connect the tree in the above step
with the new vertices. From the edges found, select the minimum edge and
add it to the tree.

o Repeat step 2 until the minimum spanning tree is formed.

Program for prim’s algorithm:


# Prims Algorithm in Python

import sys

class Graph:

def __init__(self, vertexNum, edges, nodes):

self.edges = edges

self.nodes = nodes

self.vertexNum = vertexNum
self.MST = []

def printSolution(self):

print("Edge : Weight")

for s, d, w in self.MST:

print("%s -> %s: %s" % (s, d, w))

def primsAlgo(self):

visited = [0]*self.vertexNum

edgeNum=0

visited[0]=True

while edgeNum<self.vertexNum-1:

min = sys.maxsize

for i in range(self.vertexNum):

if visited[i]:

for j in range(self.vertexNum):

if ((not visited[j]) and self.edges[i][j]):

if min > self.edges[i][j]:

min = self.edges[i][j]

s=i

d=j

self.MST.append([self.nodes[s], self.nodes[d], self.edges[s][d]])


visited[d] = True

edgeNum += 1

self.printSolution()

edges = [[0, 10, 20, 0, 0],

[10, 0, 30, 5, 0],

[20, 30, 0, 15, 6],

[0, 5, 15, 0, 8],

[0, 0, 6, 8, 0]]

nodes = ["A","B","C","D","E"]

g = Graph(5, edges, nodes)

g.primsAlgo()

OUTPUT:
Edge : Weight

A -> B: 10

B -> D: 5

D -> E: 8

E -> C: 6

You might also like