DATA-STUCTURES-AND-ALGORITHMS
DATA-STUCTURES-AND-ALGORITHMS
OF THE PHILIPPINES
Computer Engineering
College of Engineering
Mabini Campus, Sta. Mesa
CMPE 30052
Data Structures and Algorithms
1
Data Structures and Algorithms
CMPE 30052
Address : 1016 Anonas St., Brgy. 630, Sampaloc, Sta. Mesa, Manila
Website : https://www.pup.edu.ph
Email Address : inquire@pup.edu.ph
Tel. No. : (+63 2) 5335-1PUP (5335-1787) or 5335-1777
2
The VMPGO
VISION
PUP: The National Polytechnic University
MISSION
Ensuring inclusive and equitable quality education and promoting lifelong
learning opportunities through a re-engineered polytechnic university by committing to:
• provide democratized access to educational opportunities for the holistic
development of individuals with global perspective.
• offer industry-oriented curricula that produce highly-skilled professionals with
managerial and technical capabilities and a strong sense of public service for
nation-building.
• embed a culture of research and innovation.
• continuously develop faculty and employees with the highest level of
professionalism.
• engage public and private institutions and other stakeholders for the attainment
of social development goal.
• establish a strong presence and impact in the international academic community.
PHILOSOPHY
As a state university, the Polytechnic University of the Philippines believes that:
• Education is an instrument for the development of the citizenry and for
the enhancement of nation-building; and,
• That meaningful growth and transformation of the country are best achieved in
an atmosphere of brotherhood, peace, freedom, justice and nationalist-oriented
education imbued with the spirit of humanist internationalism.
3
SHARED VALUES AND PRINCIPLES
1. Integrity and Accountability
2. Nationalism
3. Sense of Service
4. Passion for Learning and Innovation
5. Inclusivity
6. Respect for Human Rights and the Environment
7. Excellence
8. Democracy
3. Develop and produce facilities through the use of adapted technology and
indigenous materials
PROGRAM DESCRIPTION
4
COURSE DESCRIPTION
5
COURSE LEARNING OUTCOMES (CLOS)
2. Demonstrate the ability to construct complex SQL queries to retrieve, update, and
manipulate data stored in relational databases
6
Preface
In the digital age, the ability to manage and manipulate data effectively is an invaluable skill.
The Database Management Systems (DBMS) instructional material you are about to explore
is designed to provide a thorough introduction to the principles and practices that form the
backbone of data management.
This material is structured to guide students from the basics of database concepts and data
modeling to advanced topics such as transaction processing, data integrity, and distributed
databases. Each chapter is meticulously crafted to build on the previous one, ensuring a
coherent and comprehensive learning experience.
Emphasizing both theory and practice, we have included numerous examples, exercises, and
projects that encourage hands-on learning. These practical components are intended to
reinforce the theoretical knowledge and provide learners with the tools they need to tackle
real-world database challenges.
As educators and professionals in the field, our goal is to demystify the complexities of DBMS
and make this critical subject accessible to all. We trust that this material will serve as a
valuable resource in your educational journey and professional development.
7
Introduction
This course is divided into ten (10) modules. The modules provide context and
• Sorting Algorithm
• Linked list
• Queues
• Trees
• Graphs
• Complexities in Algorithms
Activities per module include Laboratory works and Machine Problems for the
8
Acknowledgements
This lecture book was accomplished with the help of the following students:
9
Table of Contents
Title Page 1
The VMPGO 3
Preface 7
Table of Contents 10
d. Algorithm 22
Activity 22
1.1 Lists 25
Activity 30
Activity 43
10
3.1 Overview of OOP Terminologies 45
Activity 51
Activity 61
Activity 71
11
6.1 Stacks 72
6.6 Backtracking 77
Activity 78
Lesson 7: Queues 80
Activity 84
Lesson 8: Trees 85
12
8.7 Applications of Tree Data Structure 98
Activity 99
Activity 112
Summary 128
Activity 128
13
APPENDICES
List of Figures
Lesson 7: Queues
Figure 7.1.1 79
Lesson 9: Graphs
Figure 9.A 101
Figure 9.1.1 102
Figure 9.3.1 104
Figure 9.4.1 105
14
INTRODUCTION TO DATA STRUCTURES AND ALGORITHM
LEARNING OBJECTIVES
✓ To introduce the concept of data structures and its importance.
✓ To learn the difference between linear and non-linear data structure.
INTRODUCTION
What is Data Structure and its function?
• A data structure is a specialized format for processing, organizing, retrieving and
storing data in order to facilitate the access and modifications.
• A data structure may be selected or designed to store data for the purpose of
working on it with various algorithms in Computer Programming.
• Each data structure contains information about the data values, relationships
between the data and functions that can be applied to the data.
• It is a representation of data and the operations allowed on the data.
Fundamental Data Structures
15
Operations in Data Structure
• Deletion
➢ removal of a data element from a data structure if it is found.
• Insertion
➢ addition of a new data element in a data structure.
• Searching
➢ involves searching for the specified data element in a data structure.
• Traversal
➢ processing all the data elements present in it.
• Sorting
➢ arranging data elements of a data structure in a specified order.
• Merging
➢ combining elements of two similar data structures to form a new data
structure of the same type.
16
2. Stack
Stack is a linear structure that uses LIFO (Last In-First Out) rule in which the last
data added will be removed first. A push operation adds a data element in the list while a
pop operation deletes an element from the list.
3. Queue
Queue is a data structure that uses the FIFO (First In-First Out) rule in which the
first element that was added will be remove first. There are two terms used in queue, front
end, and rear. The insertion operation performed at the back end is known as enqueue,
and dequeue is the deletion operation that is performed at the front end.
4. Linked list
17
A Linked list is a collection of nodes that are made up of two parts.
Example A.1
Arrays:
1. Define the size of the array
as N
2. Create a list of N elements
where every element is zero
3. Now you can access each
element of the list using its
index ranging from [0, N-1]
Example A.2
Queues:
1. Define the sequence of elements
of the queue.
2. Create an empty list object as
variable Q and append the
elements in a sequence using
Q.append(val) method, this
operation is also called enqueuing.
3. For removing the elements use the
Q.pop(0) method of the created
object of the list which will remove
the element of the zeroth index,
this operation is also called
Dequeuing.
18
Example A.3
Stacks:
1. Define the sequence of
elements of the stack.
2. Create an empty list object as
variable S and append the
elements in a sequence using
S.append(val) method, this
operation is also called
pushing.
3. For popping the elements use
the S.pop() method of the
created object of the list which
will remove the element of the
last index.
19
Example B.1
Example B.2
Important differences between Linear Data Structures and Non-Linear Data Structures.
20
C. ABSTRACT DATA TYPES
Abstract Data Types (ADTs) are types of data whose behavior is specified by a set of
value and a set of operations. Process called abstraction and encapsulation make ADTs
beneficial.
• Abstraction refers to the organizing of solutions to a problem into well-defined
entities by defining their data and operations. It includes the replacement of low-
level details with a simpler, higher-level idea.
• Encapsulation hides the implementation and only provides well-defined
interface. It includes the building of walls or boundaries around the module’s
implementation so that the module will be only in-charge for its internal behavior
and will be independent to bugs in other parts of the system.
There are main operations that can be defined for every collection ADT:
• add data,
• remove data, and
• access data.
21
Each of these operations is executed by one or more subroutines during the
implementation. The data structures used in the implementations can be in the language
type primitive/built-
in or user-defined.
D. ALGORITHM
Algorithm is a well-ordered collection of unambiguous and effectively computable
operations that, when executed produces a result and halts in a finite amount of time. To
analyze an algorithm the most important factors is to calculate resources needed for
running the algorithm.
ACTIVITY
1. A collection of nodes that are made up of two parts.
a. Queues
b. Stacks
c. Trees
d. Linked List
4. It is a data structure that uses the FIFO (First In-First Out) rule.
a. Queue
22
b. Stacks
c. Linked List
d. Algorithm
23
c. Searching
d. Sorting
10. It is an operation of combining elements of two similar data structures to form
a new data structure of the same type.
a. Deletion
b. Insertion
c. Merging
d. Sorting
24
MODULE 1
MODULE 1
List and List Comprehension
INTRODUCTION
What comes to your mind when you hear the word “collection”? Collection is a
group of anything that may or may not share distinct characteristics. The arrangement of
elements does not have a specific order or pattern. When a collection begins to follow a
specific order, then it becomes a sequence.
A sequence is a collection of objects that contains multiple items of data arranged
in a particular manner. In Python, sequence of elements can be stored inside a list.
LEARNING OBJECTIVES
✓ To understand and be familiar with lists and list comprehension
1.1 | LISTS
A list is a type of sequence that contains elements and data. Unlike arrays, lists are
flexible in storing different data types, may it be an integer or a string. In Python, the
elements inside a list are enclosed in square brackets while being separated by a comma.
They also have a definite count, and elements can be located indexing the list. There are
many methods that can be applied while working with lists.
To create a list, simply assign a variable to an empty square bracket or use the list
function. To check for its contents, you may print the list. The index of a list starts with 0.
Index refers to the position of a certain element in the list, and we can apply it by using
the index() method. We can individually access the items in the list using indexing.
Meanwhile, we can locate the end of a list through negative indexes.
List slicing is used to get a sublist or a specific portion of items from a list. Said portion
can be anywhere for the list. The operator “:” is the most commonly used to get a range
of elements in a list. Syntax in Python is as follows:
25
Example 1.1.1
Output:
To traverse and check for the items on the list, we can use in and not in operator. This
will return a Boolean value whether or not the item has been found.
26
1.2 | LIST CONCATENATION
List concatenation is used when we have number of different lists of elements that needs
to be processed in a similar manner. There are three methods to concatenate a list:
Method #1: Using the “Naive Method”
Steps:
1. Transverse the second list
2. Append each element to the first list.
Example 1.2.1
Output:
Example 1.2.2
Output:
27
Example 1.2.3
Output:
Example 1.3.1
Two ways in creating a list
Example 1.3.2
Indexing through the list
28
Example 1.3.3
Other methods in Lists:
29
ACTIVITY
1. What are the two ways in creating a list? Choose those that apply.
a. using copy() method
b. assigning a variable to square brackets
c. using list() function
d. print(list)
2. What is the method that removes an item based on the index provided?
a. remove()
b. copy()
c. clear()
d. pop()
a. 1
b. 1, 1, 1
c. [1, 1, 1]
a. Monday
b. Thursday
c. Sunday
30
6. What do you call the operation that can get a sublist or a portion of the original
list?
a. Indexing
b. Concatenating
c. Slicing
d. Copying
a. [10,15,10]
b. [15,20]
c. [10,15]
31
MODULE 2
MODULE 2
Sorting Algorithm
INTRODUCTION
Sorting is one of the most thoroughly studied algorithms in computer science, it
refers to arranging data in a particular format and it is a basic building block that many
other algorithms are built upon. It is also used to represent data in more readable formats.
Here are some of the implementations of sorting in python: selection sort, bubble sort,
insertion sort, and merge sort.
LEARNING OBJECTIVES
✓ Students should be able to define what is sorting algorithm
✓ Visualize and learn how to sort in ascending and descending order using
Selection Sort
✓ Understand and apply Selection Sort in programming in Python
32
Example:
2 4 6 8 10 are in increasing order because every next element is
greater than the previous one
2. Decreasing Order
• The successive element is less than the current one
Example:
9 7 5 3 1 are in decreasing order, because every next element is less
than the previous one
In every iteration of selection sort, the minimum element (considering ascending order)
or maximum element (considering descending order) from the unsorted subarray is
picked and moved to the sorted subarray.
1. Start by finding the minimum value in a given list and move it to a sorted list
2. Repeat the process for each of the remaining elements in the unsorted list
3. Next element entering the sorted list is compared with the existing elements and
placed at its correct position
To arrange elements in descending order, start by finding the maximum value instead of
the minimum value.
33
Example 2.1.1
Sort the list [15, 10, 5, 3] in ascending order using Selection Sort.
Start by finding for the minimum value, which is 3, and move it to a sorted array [3]. In the
next step, the minimum value is 5 in the remaining elements of unsorted array, so we
moved it to the sorted array [3, 5]. In the third step, the minimum value is 10 in the
remaining elements of unsorted array, so we moved it to the sorted array [3, 5, 10]. In the
last step, the minimum value left in the unsorted array is 15, so we moved it to the sorted
array [3, 5, 10, 15].
Example 2.1.2
Sort the list [24, 7, 18, 26, 33, 29] in descending order using Selection Sort.
Start by finding the maximum value, which is 33, and move it to a sorted array [33]. In the
next step, the maximum value is 29 in the remaining elements of unsorted array, so we
moved it to the sorted array [33, 29]. In the third step, the maximum value is 26 in the
remaining elements of unsorted array, so we moved it to the sorted array [33, 29, 26]. In
the fourth step, the maximum value is 24 in the remaining elements of unsorted array, so
we moved it to the sorted array [33, 29, 26, 24]. In fifth step, the maximum value is 18 in
34
the remaining elements of unsorted array, so we moved it to the sorted array [33, 29, 26,
24, 18]. The only left element in the unsorted array is 7, so moving it to the sorted array
gives us [33, 29, 26, 24, 18, 7].
The Bubble Sort is considered to be the simplest sorting algorithm that works by
repeatedly swapping succeeding elements if they are in the wrong order.
LEARNING OBJECTIVES
✓ To learn how to use Bubble Sort in sorting sets of numbers
The algorithm for sorting a list in ascending order using Bubble Sort follows:
1. Compare the values of the first element and the element succeeding it and swap
their positions if the first element is greater.
2. Repeat this process through the second element until the (n + 1)th to the last
element with n being the number of iterations done to the list including the current
one as the last n – 1 element(s) is/are already in place.
3. Check if the list has already been sorted.
4. If not, repeat steps 1 – 3 until the list is sorted.
Arranging elements in descending order is also possible for this sorting algorithm. The
elements would only be swapped if the former element is less than the latter element.
35
Example 2.2.1
Using Bubble Sort, sort a list containing [5, 12, 8, 3, 9] in ascending order.
First Iteration:
Second Iteration:
Third Iteration:
Example 2.2.2
In descending order, use Bubble Sort to sort [5, 18, 16, 7, 12]
First Iteration:
36
Second Iteration:
Example 2.2.3
Sort [8, 19, 20, 16, 1] in ascending order using Bubble Sort.
First Iteration:
Second Iteration:
Third Iteration:
Fourth Iteration:
Insertion sort includes splitting a list into two, one for the sorted elements, and the other
for the unsorted elements. This sorting algorithm iterates the unsorted list from left to right
and “inserts” them into the sorted list, until the unsorted list is depleted, and all that
remains is the sorted list.
37
LEARNING OBJECTIVES
✓ Learn how to use Insertion Sort to arrange in ascending and descending order.
To arrange elements in descending order, simply change the comparison from requiring
a lesser previous element to a greater previous element in value.
Example 2.3.1
Sort the list [7, 4, 5, 3, 10] in ascending order using Insertion Sort
38
Green is the current element, and yellow is the element that has the smallest value in the
sorted list that can be compared as greater than the current element. If there are no yellow
cells in the row, the position of the current element does not move in relation to the highest
value of the sorted list, however, they are inserted at the end of the sorted list.
Example 2.3.2
Sort the list [7, 4, 5, 3, 10] in descending order using Insertion Sort.
Green is the current element, and yellow is the element that has the greatest value in the
sorted list that can be compared as lesser than the current element. If there are no yellow
cells in the row, the position of the current element does not move in relation to the lowest
value of the sorted list, however, they are inserted at the end of the sorted list.
Merge sort is a sorting technique based on divide and conquer technique. With worst-
case time complexity being Ο(n log n), it is one of the most respected algorithms. Merge
sort first divides the array into equal halves and then combines them in a sorted manner.
39
Merge sort keeps on dividing the list into equal halves until it can no more be divided. By
definition, if it is only one element in the list, it is sorted. Then, merge sort combines the
smaller sorted lists keeping the new list sorted too.
LEARNING OBJECTIVES
✓ Learn how to use Merge Sort to arrange in ascending order.
40
41
Example 2.4.1
Sort the list [14, 33, 27, 10, 35,19,42,44] in ascending order using Merge Sort.
Merge sort first divides the whole array iteratively into equal halves unless the atomic
values are achieved. We see here that an array of 8 items is divided into two arrays of
size 4.
This does not change the sequence of appearance of items in the original. Now we divide
these two arrays into halves.
We further divide these arrays and we achieve atomic value which can no more be
divided.
42
Now, we combine them in exactly the same manner as they were broken down. Please
note the color codes given to these lists.
We first compare the element for each list and then combine them into another list in a
sorted manner. We see that 14 and 33 are in sorted positions. We compare 27 and 10
and in the target list of 2 values we put 10 first, followed by 27. We change the order of
19 and 35 whereas 42 and 44 are placed sequentially.
In the next iteration of the combining phase, we compare lists of two data values, and
merge them into a list of found data values placing all in a sorted order.
After the final merging, the list should look like this −
ACTIVITY
1. Using Selection Sort, arrange the following values [3, 1, 7, 9, 15, 4] in ascending
order.
2. Using Selection Sort, arrange the following values [6, 2, 10, 18, 13, 5] in
descending order.
43
4. Using Bubble Sort, sort the following lists in ascending and descending order.
1. [5, 8, 1, 6, 3]
2. [12, 48, 23, 58, 16]
3. [158, 867, 254, 579, 957]
5. Arrange the values [23, 43, 22, 34, 32, 24] in ascending order visualized using
Insertion Sort.
6. Arrange the values [23, 43, 22, 34, 32, 24] in descending order visualized using
Insertion Sort.
8. Arrange the values [23, 43, 22, 34, 32, 24] in ascending order visualized using
Merge Sort.
9. Arrange the values [33, 53, 12, 4, 72, 1] in ascending order visualized using
Merge Sort.
10. Create a program that takes x number of integers and arranges them in
ascending order using merge sort. Output each iteration and the final output.
44
MODULE 3
MODULE 3
Python’s Object - Oriented Programming
INTRODUCTION
LEARNING OBJECTIVES
contain all the important details, parts of the house (floors, doors, windows, etc.) which
are used in building the house. The house is the object. In python, it is known as the user-
defined prototype for an object that defines a set of attributes that characterize any object
of the class.
Instantiation – this is the term used when the process of creating an instance of
a class (object).
45
Object – this is defined as a collection of data (variables) and methods (functions)
which act on those data. Like what the meaning of class above tells, the class is the
blueprint of the object. In python, it is a unique instance of a data structure which is defined
by its class. It compromises both data members which are the class variables and
In the creation of a class, a new local namespace is also created where all its
along with information about the object that each name references. Attributes may be a
data or a function. It is like a dictionary where in the keys are the object names and the
46
Example 3.2.1
47
3.4 | CREATING AN OBJECT IN PYTHON
Class objects could be used in accessing different attributes. It could also be used in
creating new object instances of that class, this process is called instantiation.
In this case, the new object instance we created named Lora which we could use to
access the attributes of objects using the object name prefix.
The use of attributes could be through a data or a method. The methods used of an object
are the corresponding functions of that class.
In the example below, Person.greet is used as a function object in other term it is called
attribute of class which means that Person.greet will be a method object.
48
The self parameter in the function definition is inside the class but the method was called
as harry.greet without the use of any argument and it still worked.
It still worked because whenever the object calls its method, the object itself is passed as
the first argument. So, harry.greet() translates into Person.greet(Lora).
Essentially, a method that is called with a list of n arguments is the same as calling the
corresponding function with an argument list that is created by inserting the object of the
method before inserting the first argument.
The function’s first argument in the class must be the object itself. This is normally called
self.
3.5 | CONSTRUCTORS IN PYTHON
The functions in a class which starts with double or two underscores (__) are called
special functions because of the meaning they have.
One constructor which is mostly used is the __init__ function. When a new object of that
class is instantiated, this special function is used.
These constructors are used in Object – Oriented Programming (OOP) to initialize all the
variables.
Example 3.5.1
49
The output will be:
The example above shows a new class which used the functions, __init__() to initialize
the variables (defaults to zero) and get_data() to display the number properly.
The new attribute, attr was also created for the object num2 and read it as well. This
attribute was created for num2 and does not affect the object num1.
3.6 | BUILT – IN CLASS ATTRIBUTES
In every Python class there are built-in class attributes which could be accessed by the
user using the dot operator which is also used in any other attributes. Here are some of
those built-in attributes:
50
Example 3.7.1
Also, by using the del statement, we could delete the object itself.
ACTIVITY
2. Object and class attributes are accessed using ___ notation in Python.
a.) Dot
b.) Def
c.) __init__
d.) Class
c.) Arff!
d.) Woof!
52
5. What is the output of the following code snippet?
53
b.) Class
c.) Instance
d.) Inheritance
54
MODULE 4
MODULE 4
Python’s Exception Handling
INTRODUCTION
4. 1 | WHAT IS EXCEPTION?
An exception is an occurrence that happens during the execution of a program that
interrupts the usual flow of instructions provided by the program. In general, an exception
occurs when a Python script faces a condition that it cannot cope with. A Python object
that contains an error is an exception. If a Python script raises an exception, it must either
handle the exception immediately or otherwise it terminates and quits.
In our programming experience so far, we have encountered several kinds of run-time
exceptions, such as division by zero, accessing a list with an out-of-range index, and
attempting to convert a non-number to an integer. To this point, all our run-time exceptions
have terminated the running program. Python offers a standard mechanism called
exception handling that assists programmers to deal with these kinds of run-time
exceptions and many more. Instead of always ending the execution of the program, as it
happens, an operating program may sense the problem and probably execute code to fix
the problem or minimize it in other ways. In Python, this chapter discusses handling
exceptions.
LEARNING OBJECTIVES
✓ Define what is Exception handling using Python.
✓ Learn how to apply Exceptions in Python.
55
Example 4.2.1
Output
In this program, we loop through the values of the randomList list. As previously
mentioned, the portion that can cause an exception is placed inside the try block. If no
exception occurs, the except block is skipped and normal flow continues (for last value).
But if any exception
occurs, it is caught by the except block (first and second values). Here, we print the name
of the exception using the exc_info() function inside sys module. We can see that a
causes ValueError and 0 causes ZeroDivisionError. Since every exception in Python
inherits from the base Exception class, we can also perform the above task in the
following way:
56
Example 4.2.2
57
Example 4.3.1
58
4.6 | TRY WITH ELSE CLAUSE IN PYTHON
In some situations, you might want to run a certain block of code if the code block inside
try ran without any errors. For these cases, you can use the optional else keyword with
the try statement.
Note: Exceptions in the else clause are not handled by the preceding except clauses.
Example 4.6.1
59
Output
4.7 | TRY-FINALLY
The try statement in Python can have an optional finally clause. This clause is executed
no matter what and is generally used to release external resources. For example, we may
be connected to a remote data center through the network or working with a file or a
Graphical User Interface (GUI). In all these circumstances, we must clean up the resource
before the program comes to a halt whether it successfully ran or not. These actions
(closing a file, GUI or disconnecting from network) are performed in the finally clause to
guarantee the execution.
This type of construct makes sure that the file is closed even if an exception occurs during
the program execution
60
4.8 | EXCEPTION HIERARCHY
Exception handling occurs based on an exception hierarchy, determined by the
inheritance structure of the exception classes. For example, IOError and OSError are
both subclasses of EnvironmentError. Code that catches an IOError will not catch an
OSError. However, code that catches an EnvironmentError will catch both IOErrors and
OSErrors.
ACTIVITY
1. How many except statements can a try-except block have?
a) zero
b) one
c) more than one
d) more than zero
61
a) NameError
b) ValueError
c) IndexError
d) TypeError
4. The following Python code will result in an error if the input value is entered as
-5.
a) True
b) False
a) No output
b) after f?
c) error
d) after f
62
7. Compare the following two Python codes shown below and state the output if
the input entered in each case is -6?
a) ValueError, NameError
b) AttributeError, ValueError
c) NameError, TypeError
d) TypeError, ValueError
a) 1
b) 2
c) 3
d) error, there is more than one return statement in a single try-finally block
63
a) someError has occurred
b) someError has not occurred
c) invalid code
d) none of the mentioned
64
MODULE 5
MODULE 5
Linked Lists
INTRODUCTION
5.1 | WHAT ARE LINKED LISTS?
Linked lists are a sequence of data elements, which are connected together via
links. Each data element contains a connection to another data element in the form of a
pointer. Python does not have linked lists in its standard library. However, one may
implement the concept of linked lists using the concept of nodes.
LEARNING OBJECTIVES
✓ Define what are Linked Lists.
✓ Identify the differences between Linked Lists and Array.
Figure 5.1.1 The elements in Linked Lists are linked using pointers
Basic Operations
Following are the basic operations supported by a list.
●Insertion − add an element at the beginning of the list.
●Deletion − delete an element at the beginning of the list.
●Display − displaying complete list.
●Search − search an element using a given key.
●Delete − delete an element using a given key.
65
5.2 | DIFFERENCE BETWEEN LINKED LISTS AND ARRAYS
Like arrays, Linked List is a linear data structure. However, elements in a linked
list are not stored at a contiguous location (a single memory space).
66
●Dynamic size
●Ease of insertion or deletion
Drawbacks
●Random access is not allowed. We have to access elements sequentially starting from
the first node. cannot do binary search with linked lists efficiently with its default
implementation.
●Extra memory space for a pointer is required with each element of the list.
●Not cache friendly. Since array elements are contiguous locations, there is locality of
reference which is not there in case of linked lists.
67
Figure 5.4.1 How to insert an element to the back end of a Linked List using
Python
Figure 5.4.2 Result after using the code in Figure 5.4.1 to insert an element at the
rear of the list
68
Figure 5.4.3 How to insert an element between two nodes using Python
69
Figure 5.4.5. Code which removes a node in a Linked List
70
ACTIVITY
1. Explain linked list.
2. Explain Insertion and Deletion.
3. Which of the following points is/are true about Linked List data structure when it is
compared with an array?
A. Arrays have better cache locality that can make them better in terms of
performance.
B. It is easy to insert and delete elements in Linked List.
C. Random access is not allowed in a typical implementation of Linked List.
D. The size of the array must be pre-decided, linked lists can change their size
any time.
E. All of the above
4. Which of the following sorting algorithms can be used to sort a random linked list
with minimum time complexity?
A. Insertion Sort
B. Quick Sort
C. Heap Sort
D. Merge Sort
71
MODULE 6
MODULE 6
Stacks and Its Application
INTRODUCTION
Stacking is a process in which objects are arranged in a neat or orderly pile. In real
life, the process of stacking or creating a stack is always applied to almost any
arrangement of objects, from the simplest such as clothes and books, to the most complex
such as virtual objects such as data.
LEARNING OBJECTIVES
✓ To understand the Stack data structure in Python.
6.1 | STACKS
In the field of computer science, the application of stack is one of the important
and very essential ways in creating data structures. The collected data is to be piled in
order in which the addition/insertion and the deletion of a data item can only be done at
one end or also called the top of the stack. To achieve that way of only inserting and
deleting an item from the top of the stack, the last-in-first-out process is done meaning
the last item element that was added will also be the first item element to be removed.
A LIFO structure where elements are inserted and deleted from the same end. It
is a simple memory mechanism where elements are implemented as an array or linked
list.
A stack is a limited access data structure - elements can be added and removed
from the stack only at the top. push adds an item to the top of the stack, pop removes the
72
item from the top. A helpful analogy is to think of a stack of books; you can remove only
the top book, also you can add a new book on the top.
Figure 6.2.1. This illustration represents how data moves inside a stack using the
operation Push and Pop. You can also observe the LIFO principle is being applied
since the first number.
73
language (0 and 1). It assumes that an arithmetic operation can take place in two
operands only like A+B, C*D, F/E etc. But the arithmetic expression may contain more
than one operator and many operands like A+(B+C-(D/E^F)*G)*H). These complex
expressions can be converted and can be executed in two operands and an operator
form.
●Infix Expression
In Infix, the operator is preceded and succeeded by an operand.
Example: A+B
●Postfix Expression
In Postfix, the operator is succeeded by both the operands.
Example: AB+
^ Exponential
*, / Multiplication, Division
+, - Addition, Subtraction
Example 6.3.1.
Conversion of infix to postfix expression:
●A+(B+C*D)+E → ABCD*+*E+
74
Solution:
Example 6.3.2.
Computation to postfix expression:
●2 3 4 + * 5 * → 70
Solution:
75
6.4 | REVERSING STRINGS
It is one of the simple applications of stack, where characters of string are pushed
onto the stack one by one as the string is read from left to right. Once all characters are
pushed, they are popped out. Since the last character pushed in is the first to be popped
out. Subsequent pop operation results in reversal of strings.
Time Complexity: O (n) where n is the number of characters in stack.
Example 6.4.1.
76
Figure 6.5.1
6.6 | BACKTRACKING
It is a general algorithm to find solutions to some computational problems. Let us
consider a simple example of a maze. There are several points from starting to
destination. If a random path is chosen, suppose it is wrong there must be a way to reach
the beginning of a point. With stacks, we remember the point where we reached by
pushing that point to stack. In case of a wrong path, pop the last point from the stack and
thus return to the last point and continue to find the right path.
77
ACTIVITY
1. It is the procedure of arranging objects or data in an orderly manner.
a. Array c. Stacking
b. Queue-ing d. Sorting
2. What insert and delete principle does the Stacks use?
a. Last in, First out c. First in, Last out
b. Last in, Last out d. First in, First out
3. Which method inserts an element?
a. Pop c. Insert
b. Push d. Append
4. Which method looks at the top element?
a. Push c. isEmpty
b. Pop d. peak
78
a. Overflow c. Empty
b. Underflow d. Crash
79
MODULE 7
MODULE 7
Queues
INTRODUCTION
7.1 | WHAT IS QUEUE?
Queue is an abstract data type or a linear data structure, just like stack data
structure, in which the first element is inserted from one end called the REAR(also called
tail), and the removal of the existing element takes place from the other end called
FRONT(also called head). This makes the queue a FIFO (First in First Out) data structure,
which means that element inserted first will be removed first. Which is exactly how the
queue system works in the real world. If you go to a ticket counter to buy movie tickets,
and you are first in the queue, then you will be the first one to get the tickets. The same
case goes for the Queue data structure. Data inserted first, will leave the queue first.
LEARNING OBJECTIVES
✓ To learn about queues in Python.
✓ To know the basic operations, steps, functions and applications of queue.
The process to add an element into the queue is called Enqueue and the process
of removal of an element from the queue is called Dequeue.
80
7.2 | STEPS OF ENQUEUE OPERATION
1. Check if the queue is full or not.
2. If the queue is full, then print an overflow error and exit the program.
3. If the queue is not full, then increment the rear pointer to point to the next empty
space.
4. Add a data element to the queue location, where the rear is pointing.
5. Return success.
81
2. Input Restricted Queue – in this type of Queue, the input can be taken from one
side only (rear) and deletion of elements can be done from both sides (front and rear).
This kind of Queue does not follow FIFO (First in First Out) principle. This queue is used
in the cases where the consumption of the data needs to be FIFO order but and if there
is a need to remove the recently inserted data for some reasons, such cases can be
irrelevant data, performance issues, etc.
3. Output Restricted Queue – In this type of Queue, the input can be taken from both
sides (rear and front) and the deletion of the element can be done from only one side
(front). This queue is used in the case where the inputs have some priority order to be
executed and the input can be placed even in the first place so that it is executed first.
4. Double ended Queue (Dequeue) – Double Ended Queue is also a Queue data
structure in which the insertion and deletion are performed at both the ends (front
and rear). That means, we can insert at both front and rear positions and can delete
from both front and rear positions.
5. Priority Queue – A priority queue is a special type of queue in which each element is
associated with a priority. There are two types of Priority Queues these are:
1. Ascending Priority Queue – Element can be inserted arbitrarily but only
the smallest element can be removed. For example, suppose there is an array
having elements 8, 4, 16 in the same order. So, while inserting the elements,
the insertion will be in the same sequence but while deleting the order will be
4, 8, 16.
2. Descending priority Queue – Elements can be inserted arbitrarily but only the
largest element can be removed first from the given Queue. For example,
suppose there is an array having elements 2, 1, 4 in the same order. So, while
inserting the elements, the insertion will be in the same sequence but while
deleting, the order will be 4, 2, 1.
82
2. In real life scenarios, Call Center phone systems use Queues to hold people
calling them in an order, until a service representative is free.
3. Handling of interrupts in real-time systems. The interrupts are handled in the
same order as they arrive i.e First come first served.
7.8 | IMPLEMENTATION OF QUEUE DATA STRUCTURE USING ARRAY
Queue can be implemented using an Array, Stack or Linked List. The easiest way
of implementing a queue is by using an Array. Initially the head (FRONT) and the tail
(REAR) of the queue points at the first index of the array (starting the index of array from
0). As we add elements to the queue, the tail keeps on moving ahead, always pointing to
the position where the next element will be inserted, while the head remains at the first
index.
Example 7.8.1.
83
ACTIVITY
1. Given a circular array-based queue Q capable of holding 7 objects. Show the final
contents of the array after the following code is executed:
Final Answer:
0 1 2 3 4 5 6
2. Given a 5-element queue Q (from front to back: 1, 3, 5, 7, 9), and an empty stack S,
remove the elements one-by-one from Q and insert them into S, then remove them one-
by-one from S and re-insert them into Q. The queue now looks like? (from front to back)
84
MODULE 8
MODULE 8
Trees
INTRODUCTION
Tree is a kind of a nonlinear data structure where each node in it contains data
and a number of pointers to other nodes in the structure such that no loops exist.
Compared to a real-life tree, it has a root, branches, and leaves. A tree with no any node
is called an empty tree and a singular-node tree for a tree with one node only (that node
is the root node). In programming, there are two main classification of tree data structure.
First one is the General Tree and the other one is the Binary Tree. A General Tree is a
tree where in each node could have zero (0) or more children. On the other hand, a Binary
Tree is a tree where in each node could have no more than two (2) children. However, in
this learning material, we will focus on Binary trees.
LEARNING OBJECTIVES
✓ To understand the Tree data structure in Python.
✓ To learn about Binary Tree data structure.
➤ Leaf Node: is the node that does not have a child node.
➤ Sibling Nodes: are the nodes that come from the same parent node.
85
➤ Ancestor Node: is the node which is connected to all lower-level nodes.
➤ Descendant Node: is the inverse relationship of ancestor nodes. It is the child of the
child node.
➤ Level: is the number of edges from one node to the root node. (Note: The level of the
root node is zero (0))
➤ Height of the Tree: is the number of nodes on the longest path from root to a leaf.
The figure shown above is an example of a binary tree. The following are the
description of the shown binary tree:
• Root Node: 25
• Leaf nodes or the leaves: 5, 12, 22, 28, 38, and 48
• Node 25: Level 0
• Nodes 20 and 36: Level 1
• Nodes 10, 22, 30 and 40: Level 2
• Nodes 5, 12, 28, 38 and 48: Level 3
86
• Height of the tree: 4
Example 8.3.2
Given this following set of numbers: 40, 10, 65, 88, 70, 21, 140, 29, 13, 1, 67
Construct the corresponding binary tree.
87
8.4 | BINARY TREE TRAVERSALS
Unlike other data structures which only have one logical way to traverse them,
trees can be traversed in three (3) different orders:
1. Inorder Traversal
2. Preorder Traversal
3. Postorder Traversal
A. In-order Traversal
In the case of Binary Search Trees (BST), Inorder Traversal firstly explores the left
subtree, followed by the root, and lastly, the right subtree. In short, we will follow this
scheme:
Left – Root – Right
B. Pre-order Traversal
In the case of Binary Search Trees (BST), Preorder Traversal firstly explores the
root, followed by the left subtree, and lastly, the right subtree. In short, we will follow this
scheme:
Root – Left – Right
C. Post-order Traversal
In the case of Binary Search Trees (BST), Inorder Traversal firstly explores the left
subtree, followed by the right subtree, and lastly, the root. In short, we will follow this
scheme:
Left – Right – Root
88
Example 8.4.1
In-order Traversal: 1 3 4 6 7 8 10 13 14
1. Starting from the left subtree: 3 1 6 4 7
2. 1 is the left element of the 3 1 6 4 7 left subtree >>> 1 _ _ _ _ _ _ _ _
3. 3 is the root node of the 3 1 6 4 7 left subtree >>> 1 3 _ _ _ _ _ _ _
4. 6 4 7 right subtree is the right element of the 3 1 6 4 7 left subtree.
5. From 6 4 7 right subtree, 4 is the left element >>> 1 3 4 _ _ _ _ _ _
6. From 6 4 7 right subtree, 6 is the root node >>> 1 3 4 6 _ _ _ _ _
7. From 6 4 7 right subtree, 7 is the right element >>> 1 3 4 6 7 _ _ _ _
8. 8 is the root node of 3 1 6 4 7 left and 10 14 13 right subtree >>> 1 3 4 6 7 8 _ _ _
9. From 10 14 13 right subtree, there is neither left element nor left subtree.
10. 10 is the root node >>> 1 3 4 6 7 8 10 _ _
11. 14 13 right subtree is the right element of the 10 14 13 right subtree.
12. From 14 13 right subtree, 13 is the left element >>> 1 3 4 6 7 8 10 13 _
13. From 14 13 right subtree, 14 is the root node >>> 1 3 4 6 7 8 10 13 14
14. In-order Traversal: 1 3 4 6 7 8 10 13 14
89
Example 8.4.2
Pre-order Traversal: 8 3 1 4 16 10 9 13 30 24 32
90
Example 8.4.3
Post-order Traversal: 7 17 27 22 15 45 75 60 30
91
8.5 | ARITHMETIC EXPRESSION TREE
The arithmetic expression tree is a binary tree where all internal nodes correspond
to the operator and all external nodes correspond to the operand. When the given
arithmetic expression tree was traversed in in-order, the output will be an in-fix
expression. When the given arithmetic expression tree was traversed in post-order, the
output will be a post-fix expression. Lastly, when the given arithmetic expression tree was
traversed in pre-order, the output will be a pre-fix expression.
Example 8.5.1
The arithmetic expression tree of: 3 + [(a + 5) * b]
Example 8.5.2
The arithmetic expression tree of: [x * (y – 2)] / [6 / (z + 1)]
93
In-order Traversal (In-fix expression): [x * (y – 2)] / [6 / (z + 1)]
Post-order Traversal (Post-fix expression): x y 2 - * 6 z 1 + / /
Pre-order Traversal (Pre-fix expression): / * x – y 2 / 6 + z 1
94
19. Since (+ z 1) right subtree, put it inside a parenthesis >>> (x * (y – 2)) / (6 / (z + 1))
20. Fix the enclosure of the elements. >>> [x * (y – 2)] / [6 / (z + 1)]
21. In-order Traversal (In-fix expression): [x * (y – 2)] / [6 / (z + 1)]
B. Post-order Traversal (Post-fix expression):
1. From (/ * x – y 2 / 6 + z 1) tree, (* x – y 2) left subtree is the left element of / root.
2. From (* x – y 2) left subtree, x is the left element of * root >>> x _ _ _ _ _ _ _ _ _ _
3. From (* x – y 2) left subtree, (– y 2) right subtree is the right element of * root.
4. From (– y 2) right subtree, y is the left element of – root >>> x y _ _ _ _ _ _ _ _ _
5. From (– y 2) right subtree, 2 is the right element of – root >>> x y 2 _ _ _ _ _ _ _ _
6. From (– y 2) right subtree, – is the root node >>> x y 2 – _ _ _ _ _ _ _
7. From (* x – y 2) left subtree, * is the root node >>> x y 2 – * _ _ _ _ _ _
8. From (/ * x – y 2 / 6 + z 1) tree, (/ 6 + z 1) right subtree is the right element of / root.
9. From (/ 6 + z 1) right subtree, 6 is left element of / root >>> x y 2 – * 6 _ _ _ _ _
10. From (/ 6 + z 1) right subtree, (+ z 1) right subtree is the right element of / root.
11. From (+ z 1) right subtree, z is the left element of + root >>> x y 2 – * 6 z _ _ _ _
12. From (+ z 1) right subtree, 1 is the right element >>> x y 2 – * 6 z 1 _ _ _
13. From (+ z 1) right subtree, + is the root node >>> x y 2 – * 6 z 1 + _ _
14. From (/ 6 + z 1) right subtree, / is the root node >>> x y 2 – * 6 z 1 + / _
15. From (/ * x – y 2 / 6 + z 1) tree, / is the root node >>> x y 2 – * 6 z 1 + / /
16. Post-order Traversal (Post-fix expression): x y 2 - * 6 z 1 + / /
C. Pre-order Traversal (Pre-fix expression):
1. From (/ * x – y 2 / 6 + z 1) tree, / is the root node >>> / _ _ _ _ _ _ _ _ _ _
2. From (/ * x – y 2 / 6 + z 1) tree, (* x – y 2) left subtree is the left element of / root.
3. From (* x – y 2) left subtree, * is the root node >> / * _ _ _ _ _ _ _ _ _
4. From (* x – y 2) left subtree, x is the left element of * root >> / * x _ _ _ _ _ _ _ _
5. From (* x – y 2) left subtree, (– y 2) right subtree is the right element of * root.
6. From (– y 2) right subtree, – is the root node >>> / * x – _ _ _ _ _ _ _
7. From (– y 2) right subtree, y is the left element of – root >>> / * x – y _ _ _ _ _ _
8. From (– y 2) right subtree, 2 is the right element of – root >>> / * x – y 2 _ _ _ _ _
9. From (/ * x – y 2 / 6 + z 1) tree, (/ 6 + z 1) right subtree is the right element of / root.
10. From (/ 6 + z 1) right subtree, / is the root node >>> / * x – y 2 / _ _ _ _
11. From (/ 6 + z 1) right subtree, 6 is left element of / root >>> / * x – y 2 / 6 _ _ _
12. From (/ 6 + z 1) right subtree, (+ z 1) right subtree is the right element of / root.
95
13. From (+ z 1) right subtree, + is the root node >>> / * x – y 2 / 6 + _ _
14. From (+ z 1) right subtree, z is the left element of + root >>> / * x – y 2 / 6 + z _
15. From (+ z 1) right subtree, 1 is the right element >>> / * x – y 2 / 6 + z 1
16. Pre-order Traversal (Pre-fix expression): / * x – y 2 / 6 + z 1
Example 8.6.1
The following is a sample Python program which demonstrates how to insert data
in a Binary Search Tree, and how to traverse that tree in pre-order, post-order, and in-
order.
Source Code
96
97
8.7 | APPLICATIONS OF TREE DATA STRUCTURE
The following are some of the applications of tree data structure:
• Manipulate hierarchical data.
• Make information easy to search (see tree traversal).
• Manipulate sorted lists of data.
• As a workflow for compositing digital images for visual effects.
• Router algorithms or the networks to find the best path on the Internet.
• Form of a multi-stage decision-making (see business chess).
• Getting data from API via token-based authentication.
98
ACTIVITY
1. How many root/s is/are there in every tree?
a. 2 c. 1
b. Depends on the type of the tree d. 0
2. It is the node which acts as a child of a child node.
a. Child Node c. Sibling Node
b. Leaf Node d. Descendant Node
3. It is a node-based binary tree data structure.
a. Binary Search Tree c. Complete Binary Tree
b. Full Binary Tree d. Binary Select Tree
4. The _____________ of a node contains only nodes with keys greater than the
node’s key.
a. Left subtree c. Right subtree
b. Binary Tree d. Duplicate Node
5. Which of the following code snippets performs in-order traversal?
a. public void inorder(Tree root) c. public void inorder(Tree root)
{ {
System.out.println(root.data); inorder(root.right);
inorder(root.left); inorder(root.left);
inorder(root.right); System.out.println(root.data);
} }
99
For number 7-9:
100
MODULE 9
MODULE 9
Graphs
INTRODUCTION
In this chapter we will study graphs. Graphs are a more general structure than the
trees we studied in the last chapter; in fact, you can think of a tree as a special kind of
graph. Graphs can be used to represent many interesting things about our world,
including systems of roads, airline flights from city to city, how the Internet is connected,
or even the sequence of classes you must take to complete a major in computer science.
We will see in this chapter that once we have a good representation for a problem, we
can use some standard graph algorithms to solve what otherwise might seem to be a very
difficult problem.
While it is relatively easy for humans to look at a road map and understand the
relationships between different places, a computer has no such knowledge. However, we
can also think of a road map as a graph. When we do so we can have our computer do
interesting things for us. If you have ever used one of the Internet map sites, you know
that a computer can find the shortest, quickest, or easiest path from one place to another.
As a student, you may wonder about the courses you must take in order to get a
major. A graph is a good way to represent the prerequisites and other interdependencies
among courses. Figure 1 shows another graph. This one represents the courses and the
order in which they must be taken to complete a major in computer science.
LEARNING OBJECTIVES
✓ To learn what a graph is and how it is used.
✓ To implement the graph abstract data type using multiple internal
representations.
✓ To see how graphs can be used to solve a wide variety of problems.
101
Figure A. Prerequisites for a Computer Science Major
Detailed Explanation:
102
Figure 9.1.1 A Simple example of a Directed Graph
The example graph in Figure 9.1.1 helps illustrate two other key graph terms:
• Path
- A path in a graph is a sequence of vertices that are connected by edges. Formally
we would define a path as w1, w2, ..., wn such that (wi, wi +1) ∈ E for all 1 ≤ i ≤ n−1. The
unweighted path length is the number of edges in the path, specifically n−1.
- The weighted path length is the sum of the weights of all the edges in the path.
- For example in Figure 9.2 the path from V3 to V1 is the sequence of vertices
(V3,V4,V0,V1). The edges are {(v3, v4,7), (v4, v0,1), (v0, v1,5)}.
• Cycle
- A cycle in a directed graph is a path that starts and ends at the same vertex.
For example, in Figure 9.2 the path (V5, V2, V3, V5) is a cycle.
103
- A graph with no cycles is called an acyclic graph.
- A directed graph with no cycles is called a directed acyclic graph or a DAG.
We will see that we can solve several important problems if the problem can
be represented as a DAG.
Beginning with the formal definition for a graph there are several ways we can
implement the graph ADT in Python. We will see that there are trade-offs in using different
representations to implement the ADT described above. There are two well-known
implementations of a graph, the adjacency matrix and the adjacency list. We will explain
both of these options, and then implement one as a Python class.
104
Figure 9.3.1 An Adjacency Matrix Representation for a Graph
The advantage of the adjacency matrix is that it is simple, and for small graphs it
is easy to see which nodes are connected to other nodes. However, notice that most of
the cells in the matrix are empty. Because most of the cells are empty, we say that this
matrix is “sparse.” A matrix is not a very efficient way to store sparse data. In fact, in
Python you must go out of your way to even create a matrix structure like the one in Figure
9.3.
The adjacency matrix is a good implementation for a graph when the number of
edges is large. But what do we mean by large? How many edges would be needed to fill
the matrix? Since there is one row and one column for every vertex in the graph, the
number of edges required to fill the matrix is | V | 2 . A matrix is full when every vertex is
connected to every other vertex. There are few real problems that approach this sort of
connectivity. The problems we will look at in this chapter all involve graphs that are
sparsely connected.
105
9.4 | ADJACENCY LIST
A more space-efficient way to implement a sparsely connected graph is to use an
adjacency list. In an adjacency list implementation, we keep a master list of all the vertices
in the Graph object and then each vertex object in the graph maintains a list of the other
vertices that it is connected to. In our implementation of the Vertex class we will use a
dictionary rather than a list where the dictionary keys are the vertices, and the values are
the weights. Figure 9.4 illustrates the adjacency list representation for the graph in Figure
9.2.
106
Example 9.5.1
Using dictionaries, it is easy to implement the adjacency list in Python. In our
implementation of the Graph abstract data type we will create two classes (see Listing 1
and Listing 2), Graph, which holds the master list of vertices, and Vertex, which will
represent each vertex in the graph.
Each Vertex uses a dictionary to keep track of the vertices to which it is connected,
and the weight of each edge. This dictionary is called connectedTo. The listing below
shows the code for the Vertex class. The constructor simply initializes the id, which will
typically be a string, and the connectedTo dictionary. The addNeighbor method is used
to add a connection from this vertex to another. The getConnections method returns all
of the vertices in the adjacency list, as represented by the connectedTo instance
variable. The getWeight method returns the weight of the edge from this vertex to the
vertex passed as a parameter.
Listing Explanation:
The Graph class, shown in the next listing, contains a dictionary that maps vertex
names to vertex objects. In Figure 9.4, this dictionary object is represented by the shaded
107
gray box. Graph also provides methods for adding vertices to a graph and connecting
one vertex to another.
The getVertices method returns the names of all of the vertices in the graph. In
addition, we have implemented the __iter__ method to make it easy to iterate over all the
vertex objects in a particular graph. Together, the two methods allow you to iterate over
the vertices in a graph by name, or by the objects themselves.
Using the Graph and Vertex classes just defined, the following Python session
creates the graph in Figure 9.2.
108
Steps to reproduce:
First, we create six vertices numbered 0 through 5. Then we display the vertex
dictionary. Notice that for each key 0 through 5 we have created an instance of a Vertex.
Next, we add the edges that connect the vertices together. Finally, a nested loop verifies
that each edge in the graph is properly stored. You should check the output of the edge
list at the end of this session against Figure 9.2.
Example 9.5.1
Example 9.5.2
In this example, we will see how to implement graphs in Python using dictionary
data structure in Python. The keys of the dictionary used are the nodes of our graph and
the corresponding values are lists with each node, which are connected by an edge.
This simple graph has six nodes (a-f) and five arcs:
109
a -> c
b -> c
b -> e
c -> a
c -> b
c -> d
c -> e
d -> c
e -> c
e -> b
It can be represented by the following Python data structure. This is a dictionary
whose keys are the nodes of the graph. For each key, the corresponding value is a list
containing the nodes that are connected by a direct arc from this node.
Python representation:
110
Python program for validation of a graph:
111
ACTIVITY
1. Which of the following statements for a simple graph is correct?
a) Every path is a trail.
b) Every trail is a path.
c) Every trail is a path as well as every path is a trail.
d) Path and trail have no relation.
2. In a simple graph, the number of edges is equal to twice the sum of the degrees of
the vertices.
a) True
b) False
3. Which of the following properties does a simple graph not hold?
a) Must be connected
b) Must be unweighted
c) Must have no loops or multiple edges
d) Must have no multiple edges
4. For a given graph G having v vertices and e edges which is connected and has no
cycles, which of the following statements is true?
a) v=e c) v + 1 = e
b) v = e+1 d) v = e-1
5. In a simple graph, the number of edges is equal to twice the sum of the degrees of
the vertices.
a) True
b) False
6. What is the number of edges present in a complete graph having n vertices?
a) (n*(n+1))/2 c) n
b) (n*(n-1))/2 d) Information given is insufficient
7. Which of the following is true?
a) A graph may contain no edges and many vertices.
b) A graph may contain many edges and no vertices.
c) A graph may contain no edges and no vertices.
d) A graph may contain no vertices and many edges.
112
8. If a simple graph G, contains n vertices and m edges, the number of edges in the
Graph G'(Complement of G) is ___________
a) (n*n-n-2*m)/2 c) (n*n-n-2*m)/2
b) (n*n+n+2*m)/2 d) (n*n-n+2*m)/2
9. The given Graph is regular.
a) True
b) False
10. For the given graph(G), which of the following statements is true?
a) G is a complete graph.
b) G is not a connected graph.
c) The vertex connectivity of the graph is 2.
d) The edge connectivity of the graph is 1.
113
MODULE 10
MODULE 10
Complexities in Algorithm
114
Several algorithms for solving the same problem may exist - with different
properties. Same algorithm may be represented in different ways showing the flexibility
of the algorithm.
LEARNING OBJECTIVES
✓ Define what is Complexities in Algorithms.
115
10.3 | EFFICIENCY OF AN ALGORITHM
● What any programmer hopes to be able to do is to write successful programs, but
what sort of programs are effective? The question contributes to the notion of program
generalization.
● A property of an algorithm that refers to the amount of computing resources utilized by
the algorithm is algorithmic efficiency. To evaluate the system efficiency, an algorithm
must be evaluated, and the performance of an algorithm should be calculated depending
on the use of multiple resources.
● In addition, algorithms are applications. An algorithm is a concept that a program is
based upon. Three aspects should fulfill an algorithm:
-It should be independent of the programming language in which the idea is realized
-Every programmer having enough knowledge and experience should understand it.
-It should be applicable to inputs of all sizes.
● Algorithm efficiency denotes the pace at which a problem of size n is solved by an
algorithm.
● It is calculated by the sum of resources, time and space that it consumes. We would
like to minimize resource consumption for optimum algorithm performance. It is not
possible to compare important resources such as time and space complexity directly, so
time and space complexity should be taken into consideration for algorithmic efficiency.
● The times refers to the number of steps the algorithm executes while the space refers
to the number of unit memory storage it requires.
● The complexities of an algorithm are calculated by measuring the time taken and
space needed for the algorithm to be completed.
● One parameter is the input size, denoted by n, used to define the problem example.
● The input size n is the number of registers needed to hold the input (data segment size).
116
measure the outcome within a finite and reasonable time limit. For this cause, complexity
is asymptotically measured as infinity approaches n. Although complexity is typically in
regards to time, complexity is often evaluated in terms of space, which corresponds to
the memory requirements of the algorithm.
Compilation time is the time needed for an algorithm to be compiled. Your program
has called for syntax and semantic errors in the programs before compiling it, and
connects it to the standard libraries.
a. Worst Case: For a given problem, to achieve a desired outcome, it is the longest
time that an algorithm can use for all instances of size n.
b. Average Case: The algorithm would use the average time for a given problem to
achieve a desired outcome for all instances of size n.
c. Best Case: The algorithm would use the shortest time for a given problem to obtain a
desired outcome for all instances of size n.
117
-counting the stars in the universe
2. Space Complexity
The total amount of memory space that an algorithm/program requires, plus the
execution space of input values. So, it is enough to measure the space occupied by the
variables used in an algorithm/program to find space-complexity.
The way in which the amount of storage space is required by an algorithm varies with the
size of the problem to be solved. The space occupied by the program is generally by the
following:
The space for the computer code and the space used by the variables in the program
share a set amount of memory.
The variable amount of memory that the element variable occupies, the size of which
depends on the problem being resolved. Based on whether iterative or recursive
processes are used by the code, this space increases or decreases.
The usage of memory is not under the programmer's control, since a program totally
reliant on the compiler to allocate this memory.
But the memory space taken but the variables is in the control of a programmer. More the
number of variables used, more will be space taken by them in the memory, but the
memory space is taken, but a programmer's influence is over the
program’s variable. The higher the number of variables used, the more memory space
they can take up.
For determining the amount of memory used by the algorithm, three distinct
spaces are considered:
118
Instruction space is the amount of memory used to save the compiled version of
instructions. We consider this space as a fixed space for some value n. Normally, this
value is overlooked, but note that it is there. The instruction space is independent of the
size of the problem.
Data space is the space in memory that is used to store the data structures, amount
of space used by the variables and constants.. Memory allocated and other elements
of data. The data space is connected to the problem's scale.
Environment space is the space in the memory that is used for each function call on the
run time stack. This is connected to the run time stack and contains the previous function's
return address. As any object on the stack has a return value and a pointer on it, the
memory that each method uses on the stack is a constant. Often it is possible to call an
algorithm within another algorithm. The current variables are moved onto the system
stack in such a case, where they wait for further execution and then the call is made to
the inside algorithm.
The general step wise procedure for Big-O runtime analysis is as follows:
-Figure out what the input is and what n represents.
-Express the maximum number of operations the algorithm performs in terms of n.
-Eliminate all excluding the highest order terms.
119
-Remove all the constant factors.
3. Constant Complexity O(1)
It has the least complexity where the constant time of a constant task would not
change no matter what the input value is. Consider a feature in an array that prints a
value. Regardless of the importance of which element you ask the function to print, only
one move is needed. So we can assume that the function is running at O(1) time; it does
not increase its run-time. Its magnitude order is always 1.
4. Linear Complexity: O(n)
The running time of a linear task can vary depending on its input value. If you ask
a function to print all the elements in a 10-element array, it will take fewer steps than a
10,000 element array to complete. This is said to run at O(n); its runtime increases at a
magnitude equal to n.
5. Exponential: O(2^N)
There are a number of steps needed for a quadratic task equal to the square of its
input value. A function that takes an array and N as it is an input value where N is the
array's number of values. using a nested loop, all of which use N as their limit condition,
and I ask the function to print the contents of the array, the function will print N rounds for
a total of N^2 print steps, each round printing N rows.
6. Logarithmic: O(log(N))
O(log(n)) is more complex than O(1), The time is reduced at an inversely
proportional magnitude to N instead of increasing the time it takes to execute each
corresponding step. Only half of a progressively smaller data set per step is analyzed by
a logarithmic algorithm that conducts a binary search. The algorithm begins by looking
for half of the entire data set, an ascending ordered set of numbers. If the number is not
identified, it discards the just-checked set and then looks for half of the remaining number
set.
120
We are finding the first and the last element of the list directly by accessing the list
index. The size of the list does not have an impact on the operational efficiency of the
logic.
121
3. Quadratic Time Complexity— O(n2) (read as O of n squared)
An algorithm/code where, for each of its input, another O(n) complexity code is to
be executed. In such cases, usually, the input is traversed twice or the code has nested
loops that traverse twice of n times.
OBSERVE AND LEARN!!
We are adding all the elements of N*N matrix. Here we traverse twice N times to
reach every single element. Thus, this contains two O(n) executions, making it an O(n2)
operational execution time.
122
4. Exponentiation Time Complexity O(2n) (read as O of 2 raised to n)
-An algorithm/code where the operational time increases in exponentiations as the
input increases. The nature of these algorithms is such that every increase in the size of
input causes a huge increase in operational complexity, being low initially and then
drastically increased with the increase in the input size.
OBSERVE AND LEARN!!
We are calling the Fibonacci fun function recursively to calculate the sum of
Fibonacci numbers up to its input parameter. The complexity of execution increases in
exponentiation fashion as we call the function recursively with every increase in the size
of the input to generate the Fibonacci series.
The below table indicates the number of recursive calls per input series. As you can see
at each step the operational complexity increases almost exponentially.
123
5. Logarithmic Time Complexity O(log n) (read as O of log n)
An algorithm/code where with every iteration, the size of the relevant input
keeps on reducing. In such cases with every iteration, the relevant input keeps on
becoming smaller and smaller.
OBSERVE AND LEARN!!
Below is the code of binary search. Here, with every iteration, the search is
narrowed down to half the size of input than the size of the input in its previous iteration
as depicted in the below table.
124
6. Quasilinear Time Complexity O(n log n) (read as O of n log n)
An algorithm/code where every iteration further has a Logarithmic O(log n)
complexity.
OBSERVE AND LEARN!!
Now, we are traversing through an array and for every array item, we are calling a
binary search function that has O(log n) complexity. Thus, this code possesses a log n
complexity that has to be executed n times. Hence it has O(n log n) time complexity.
125
7. Factorial Time Complexity O(n!) (read as — O of n factorial)
-An algorithm/code where the operational execution complexity increases
factorially with the increase in the input size is said to have a Factorial Time complexity.
OBSERVE AND LEARN!!
The code below aims to find factorial of a number recursively has Factorial time
complexity as the operational execution increases exponentially as the size of input
increases. The solution to those problem statements where permutation is involved would
in most cases possess Factorial time complexity.
126
10.8 | SPACE COMPLEXITIES APPLICATION SAMPLES
As we can see in the above expression, variables a, b, c and z are all integer
types, hence they will take up 4 bytes each, so total memory requirement will be (4(4) +
4) = 20 bytes, this additional 4 bytes is for return value. And because this space
requirement is fixed for the above example, hence it is called Constant Space Complexity.
Example 10.8.1
● In the above code, 4*n bytes of space is required for the array a[] elements.
● 4 bytes each for x, n, i and the return value.
Hence the total memory requirement will be (4n + 12), which is increasing linearly
with the increase in the input value n, hence it is called Linear Space Complexity.
Similarly, we can have quadratic and other complex space complexity as well, as
the complexity of an algorithm increases.
But we should always focus on writing algorithm code in such a way that we keep
the space complexity minimum.
127
SUMMARY
Measurement of algorithm complexity should be part of the method of design. The
software developer must provide an accessible way to grasp the complexity of an
algorithm in simple terms and come up with feasible and effective ways to simplify it where
possible in order to calculate how complicated an algorithm is and to be able to spot the
necessary sections of the composition of the algorithm in order to overcome anomalies
in algorithm design and review. Without discussing the word "algorithm complexity" we
will not speak about the performance of algorithms and data structures because both are
inherently related. We consider time complexity and space complexity mainly when
evaluating an algorithm. An algorithm's time complexity quantifies the amount of time an
algorithm takes to run as a function of the input's length. Similarly, an algorithm's space
complexity quantifies the amount of space or memory an algorithm requires to run as a
function of the length of the input. It is necessary to calculate the number of operations or
steps that the algorithm will take when attempting to describe the performance of an
algorithm in terms of execution time, irrespective of some individual program or machine,
it is where the Big-O Notation is bestowed. If each of these steps is assumed to be a
simple unit of computation, then the execution time for an algorithm can be expressed as
the number of steps needed to solve the problem. It can be a difficult problem to settle on
an effective simple computation unit which would depend on how the algorithm is applied.
ACTIVITY
1. What is the worst case time complexity of a linear search algorithm?
a. O(1) c. O(log n)
b. O(n) d. O (n2)
2. An algorithm is
a. a piece of code to be executed
b. a loosely written code to make final code
c. a step by step procedure to solve problem
d. all of the above
3. What is the worst case run-time complexity of a binary search algorithm?
a. O(n2) c. O(n3)
b. O(nlog n) d. O (n)
128
4. Program with the highest run-time complexity is
a. Tower of Hanoi c. Prime Numbers
b. Fibonacci Sequence d. None of the Above
5. The two main measures for the efficiency of an algorithm are:
a. Processor and Memory c. Time and Space
b. Complexity and Capacity d. Data and Space
129