DST v2.0
DST v2.0
INDEX
Lab Course
Date Particulars Instructor Teacher
Signature Signature
Lab 8: Stack
Lab 9: Recursion
Lab 1
Programming Fundamentals with Python
Why use Python?
Python is easy to use, powerful, and versatile, making it a great choice for beginners and experts
alike. Python’s readability makes it a great first programming language — it allows you to think
like a programmer and not waste time with confusing syntax. For instance, look at the following
code to print “hello world” in Java, c# and Python.
Being a very high level language, Python reads like English, which takes a lot of syntax-learning
stress off coding beginners. Python handles a lot of complexity for you, so it is very beginner-
friendly in that it allows beginners to focus on learning programming concepts and not have to
worry about too much details.
Machine Learning
Deep Learning
Computer Vision
Data Science
Big Data Analytics
Automation Engineer
Data Analysis
Data Engineer
ETL Engineer
4. Now open command prompt (Write cmd on Run and press Enter)
5. Change directory to your desktop or where you saved your file.
6. Now write DIR and check to file exist in this folder or not.
7. Now write python and filename with extension and press enter.
Variables in Python
Variables are containers for storing data values. Unlike other programming languages, Python
has no command for declaring a variable. A variable is created the moment you first assign a
value to it. Variables do not need to be declared with any particular type and can even change
type after they have been set.
Example: 1.1
Example: 1.2
Example: 1.3
int() - constructs an integer number from an integer literal, a float literal (by rounding
down to the previous whole number), or a string literal (providing the string represents
a whole number)
float() - constructs a float number from an integer literal, a float literal or a string literal
(providing the string represents a float or an integer)
str() - constructs a string from a wide variety of data types, including strings, integer
literals and float literals
Example: 1.4
Example: 1.5
Example: 1.6
Example: 1.7
input() : This function first takes the input from the user and then evaluates the
expression, which means Python automatically identifies whether the user entered a
string or a number or list. If the input provided is not correct then either syntax error or
exception is raised by python. For example –
Example: 1.8
Example: 1.10
Example: 1.11
Example: 1.15
Example: 1.16
In a company an employee is paid as: if basic salary of employee is less than Rs. 15,000, then
Rental Allowance = 10% of basic salary and dining Allowance = 90% of basic salary. If his salary
is either equal to or above Rs. 15,000 but less than Rs. 20,000 then Rental Allowance = Rs. 500
and dining Allowance = 98% of basic salary. Write a program such that, if the employee’s salary
is input through the keyboard write a program to find his gross salary.
For loop
The for loop in Python is used to iterate over a sequence (list, tuple, string, array) or other
iterable objects. Iterating over a sequence is called traversal.
Here, val is the variable that takes the value of the item inside the sequence on each iteration.
Loop continues until we reach the last item in the sequence. The body of for loop is separated
from the rest of the code using indentation.
Example: 1.17
This function does not store all the values in memory, it would be inefficient. So it remembers
the start, stop, step size and generates the next number on the go.
Example: 1.18
for x in range(10):
print(x)
We can use the range() function in for loops to iterate through a sequence of numbers. It can
be combined with the len() function to iterate though a sequence using indexing. Here is an
example.
Example: 1.19
Example: 1.20
digits = [0, 1, 5]
for i in digits:
print(i)
else:
print("No items left.")
Here, the for loop prints items of the list until the loop exhausts. When the for loop exhausts, it
executes the block of code in the else and prints
while loop
The while loop in Python is used to iterate over a block of code as long as the test expression
(condition) is true. We generally use this loop when we don't know beforehand, the number of
times to iterate.
Syntax of while Loop in Python
while test_expression:
Body of while
In while loop, test expression is checked first. The body of the loop is entered only if the test
expression evaluates to True. After one iteration, the test expression is checked again. This
process continues until the test expression evaluates to False.
In Python, the body of the while loop is determined through indentation. Body starts with
indentation and the first unindented line marks the end.
Python interprets any non-zero value as True. None and 0 are interpreted as False.
Example: 1.21
sum = 0
i = 1
while i <= n:
sum = sum + i
i = i+1 # update counter
# print the sum
print("The sum is", sum)
In the above program, the test expression will be True as long as our counter variable i is less
than or equal to n (10 in our program).
We need to increase the value of counter variable in the body of the loop. This is very
important (and mostly forgotten). Failing to do so will result in an infinite loop (never ending
loop).
Example: 1.22
# Example to illustrate
# the use of else statement
# with the while loop
counter = 0
while counter < 3:
print("Inside loop")
counter = counter + 1
else:
print("Inside else")
Flowchart of break
Example: 1.23
Flowchart of continue
Example: 1.24
This program is same as the above example except the break statement has been replaced with
continue. We continue with the loop, if the string is "i", not executing the rest of the block.
Hence, we see in our output that all the letters except "i" gets printed.
Nested Loops:
Python programming language allows using one loop inside another loop. The following section
shows a few examples to illustrate the concept.
Syntax:
The syntax for a nested while loop statement in Python programming language is as follows:
while expression:
while expression:
statement(s)
statement(s)
Example: 1.25
Lab 1 Exercises:
Lab 2
Arrays
Arrays are vital for most programming languages. They are collections of variables, which
we call elements:
Python does not have built-in support for Arrays. But we use numpy library to use array.
An array’s elements in Python numpy library are numbered with 0, 1, 2, … N-1. Those
numbers are called indices.
The total number of elements in a given array we call length of an array.
All elements of a given array are of the same type.
This allows us to represent a group of similar elements as an ordered sequence and work
on them as a whole.
Arrays can be in different dimensions, but the most used are the one-dimensional and the
two-dimensional arrays.
One-dimensional arrays are also called vectors and two-dimensional are also known as
matrices.
Practical Demonstration
Example: 2.1
import numpy as np
FirstArray = np.array([1,2,3])
print(FirstArray[0]);
print(FirstArray[1]);
print(FirstArray[2]);
Example: 2.2
import numpy as np
FirstArray = np.arange(3)
print(FirstArray[0]);
print(FirstArray[1]);
print(FirstArray[2]);
Example: 2.3
import numpy as np
FirstArray = np.array([0 for i in range(4)])
print(FirstArray)
Example: 2.4
import numpy as np
a = np.array(['Furqan', 'Umer Aslam', 'Asim'])
print(a)
a[1] = 'Zeeshan'
print(a)
Practical Demonstration
Example: 2.5
import numpy as np
FirstArray = np.array([0 for i in range(5)])
length = len(FirstArray);
for i in range(length):
FirstArray[i] = i + 1;
for i in FirstArray:
print(i)
Example: 2.6
import numpy as np
for i in range(len(playersName)):
print()
playersName[i] = input(f"Enter Player {(i+1)} Name: ")
print("Players Name");
print("------------");
for i in playersName:
print(i)
Practical Demonstration
Example: 2.7
import numpy as np
for i in range(N):
LA[i] = int(input(f"Enter Value of Index {i}: "))
PrintArray(LA, N)
Example: 2.8
import numpy as np
Numbers = np.array([11,22,33,44,55])
for i in range(len(Numbers)):
print(Numbers[i], end=" ")
j = 0
for i in range(len(temp)):
if (i == Index):
temp[i] = value
else:
temp[i] = Numbers[j]
j += 1
Numbers = temp
for i in range(len(Numbers)):
print(Numbers[i], end=" ")
Practical Demonstration
Example: 2.9
import numpy as np
for i in range(N):
LA[i] = int(input(f"Enter value of index: {(i+1)} : "))
print("------------");
for i in range(N):
print(LA[i], end=" ")
for i in range(N):
print(LA[i], end=" ")
Example: 2.10
import numpy as np
Numbers = np.array([11,12,13,14,15])
for i in range(len(Numbers)):
print(Numbers[i], end=" ")
j = 0
for i in range(len(Numbers)):
if i == Index:
continue
else:
Temp[j] = Numbers[i]
j += 1
Numbers = Temp
for i in range(len(Numbers)):
print(Numbers[i], end=" ")
Practical Demonstration
Example: 2.11
import numpy as np
from timeit import Timer
size_of_vec = 1000
X_list = range(size_of_vec)
Y_list = range(size_of_vec)
X = np.arange(size_of_vec)
Y = np.arange(size_of_vec)
def pure_python_version():
Z = [X_list[i] + Y_list[i] for i in range(len(X_list)) ]
def numpy_version():
Z = X + Y
timer_obj1 = Timer("pure_python_version()",
"from __main__ import pure_python_version")
timer_obj2 = Timer("numpy_version()",
"from __main__ import numpy_version")
print(timer_obj1.timeit(200))
print(timer_obj2.timeit(200)) # Runs Faster!
print(timer_obj1.repeat(repeat=3, number=200))
print(timer_obj2.repeat(repeat=3, number=200)) # repeat to prove
it!
Lab 2 Exercises:
LAB 3
Multidimensional Arrays
2-dimensional arrays provide most of this capability. Like a 1D array, a 2D array is a collection of
data cells, all of the same type, which can be given a single name. However, a 2D array is
organized as a matrix with a number of rows and columns.
Example: 3.1
Numbers = array([[11,12,13],
[21,22,23],
[31,32,33]
])
print(Numbers[1,1])
Example: 3.2
print(Numbers)
Numbers[2,3] = 44;
Numbers[0,0] = 11;
print(Numbers)
Example: 3.3
for i in range(len(Players)):
for j in range(len(Players[i])):
Players[i, j] = input(f"Enter Team {i+1} Player {j+1}
Name: ")
for i in range(len(Players)):
print("Team: " + str(i+1) + " Player's Names")
for j in range(len(Players[i])):
print(Players[i, j], end=" ")
print()
Example: 3.4
Example: 3.5
from numpy import array
Numbers = array([
[
[1, 1],
[2, 2],
[3, 3]
],
[
[4, 4],
[5, 5],
[6, 6]
],
])
print(Numbers[1,2,0])
Example: 3.6
for i in range(len(ThreeDimensionalArray)):
for j in range(len(ThreeDimensionalArray[i])):
for k in range(len(ThreeDimensionalArray[i, j])):
print(ThreeDimensionalArray[i,j,k], end=" ")
print()
print()
Lab-3 Exercises:
LAB 4
Sorting Algorithms
Sorting is the process of placing elements from a collection in some kind of order. For example,
a list of words could be sorted alphabetically or by length. A list of cities could be sorted by
population, by area, or by zip code.
There are many sorting algorithms that have been developed and analyzed. This suggests that
sorting is an important area of study in computer science. Sorting a large number of items can
take a substantial amount of computing resources. Like searching, the efficiency of a sorting
algorithm is related to the number of items being processed. For small collections, a complex
sorting method may be more trouble than it is worth. The overhead may be too high. On the
other hand, for larger collections, we want to take advantage of as many improvements as
possible.
Before getting into specific algorithms, we should think about the operations that can be used
to analyze a sorting process. First, it will be necessary to compare two values to see which is
smaller (or larger). In order to sort a collection, it will be necessary to have some systematic
way to compare values to see if they are out of order. The total number of comparisons will be
the most common way to measure a sort procedure. Second, when values are not in the
correct position with respect to one another, it may be necessary to exchange them. This
exchange is a costly operation and the total number of exchanges will also be important for
evaluating the overall efficiency of the algorithm.
There are many type of sorting algorithms available on different resources. Here we are
learning some basic sorting algorithms like
1. Bubble Sort
2. Selection Sort
3. Insertion Sort
4. Counting Sort
Bubble Sort
Bubble Sort is a simple algorithm which is used to sort a given set of n elements provided in
form of an array with n number of elements. Bubble Sort compares the entire element one by
one and sort them based on their values.
If the given array has to be sorted in ascending order, then bubble sort will start by comparing
the first element of the array with the second element, if the first element is greater than the
second element, it will swap both the elements, and then move on to compare the second and
the third element, and so on.
If we have total n elements, then we need to repeat this process for n-1 times.
It is known as bubble sort, because with every complete iteration the largest element in the
given array, bubbles up towards the last place or the highest index, just like a water bubble
rises up to the water surface.
Sorting takes place by stepping through all the elements one-by-one and comparing it with the
adjacent element and swapping them if required.
i) Compare adjacent elements. If the first is greater than second, swap them.
ii) Repeat it for each adjacent pair of elements, starting with the first two and ending
with the last two. At this point the last element should be greatest.
iii) Repeat the steps for all elements except the last element.
iv) Keep repeating for one fewer elements each time until there are no pairs to
compare.
Practical Demonstration
Example: 4.1
import numpy as np
Numbers = np.array([1,5,2,4,3,6]);
print(Numbers)
print(Numbers)
Although the above logic will sort an unsorted array, still the above algorithm is not efficient
because as per the above logic, the outer for loop will keep on executing for 5 iterations even if
the array gets sorted after the second iteration.
So, we can clearly optimize our algorithm.
Hence, in the inner for loop, we check whether swapping of elements is taking place or not,
every time.
If for a particular iteration, no swapping took place, it means the array has been sorted and we
can jump out of the for loop, instead of executing all the iterations.
Let's consider an array with values {1, 5, 2, 4, 3, 6}
As we can see, in the first iteration, swapping took place, hence we updated our flag value
to true, as a result, the execution enters the for loop again. In the second iteration, swapping
took place again, hence we updated our flag value to true, as a result, the execution enters
the for loop again. But in the third iteration, no swapping will occur, hence the value of flag will
remain false, and execution will break out of loop.
Practical Demonstration
Example: 4.2
import numpy as np
Numbers = np.array([1, 5, 2, 4, 3, 6 ])
print(Numbers)
if (not flag):
break;
#Print Array
print(Numbers)
def SortStringArray(LA):
for i in range(len(LA) - 1):
for j in range(len(LA) - 1 - i):
if (LA[j][0] > LA[j+1][0]):
temp = LA[j]
LA[j] = LA[j+1]
LA[j+1] = temp
SortStringArray(Countries)
print(Countries)
Selection Sort
Selection sort is conceptually the simplest sorting algorithm. This algorithm will first find
the smallest element in the array and swap it with the element in the first position, then it will
find the second smallest element and swap it with the element in the second position, and it
will keep on doing this until the entire array is sorted.
It is called selection sort because it repeatedly selects the next-smallest element and swaps it
into the right place.
Following are the steps involved in Selection Sort (for sorting a given array in ascending order):
i) Starting from the first element, we search the smallest element in the array, and
replace it with the element in the first position.
ii) We then move on to the second position, and look for smallest element present in
the subarray, starting from index 1, till the last index.
iii) We replace the element at the second position in the original array, or we can say at
the first position in the subarray, with the second smallest element.
iv) This is repeated, until the array is completely sorted.
Practical Demonstration
Example: 4.4
import numpy as np
Numbers = np.array([1,5,2,4,3,6])
print(Numbers)
if Numbers[i] != Numbers[index]:
temp = Numbers[i]
Numbers[i] = Numbers[index]
Numbers[index] = temp
print(Numbers)
Example: 4.5
TowDArray = array([
["5122", "Furqan"],
["6123", "Umer"],
["5111", "Ali"]
])
print(TowDArray)
temp = array(TowDArray[index])
TowDArray[index] = TowDArray[i]
TowDArray[i] = temp;
print(TowDArray)
Insertion Sort
Consider you have 10 cards out of a deck of cards in your hand. And they are sorted, or
arranged in the ascending order of their numbers. If I give you another card, and ask you
to insert the card in just the right position, so that the cards in your hand are still sorted. What
will you do?
Well, you will have to go through each card from the starting or the back and find the right
position for the new card, comparing its value with each card. Once you find the right position,
you will insert the card there. Similarly, if more new cards are provided to you, you can easily
repeat the same process and insert the new cards and keep the cards sorted too.
This is exactly how insertion sort works. It starts from the index 1 (not 0), and each index
starting from index 1 is like a new card, that you have to place at the right position in the sorted
subarray on the left.
Algorithm Steps of Insertion Sort
Following are the steps involved in Insertion Sort (for sorting a given array in ascending order):
i) We start by making the second element of the given array, i.e. element at index 1,
the key. The key element here is the new card that we need to add to our existing
sorted set of cards (remember the example with cards above).
ii) We compare the key element with the element(s) before it, in this case, element at
index 0:
iii) If the key element is less than the first element, we insert the key element before
the first element.
iv) If the key element is greater than the first element, then we insert it after the first
element.
v) Then, we make the third element of the array as key and will compare it with
elements to its left and insert it at the right position.
vi) And we go on repeating this, until the array is sorted.
Practical Demonstration
Example: 4.6
from numpy import array
print(Numbers)
Example: 4.7
StudentNames[index] = temp
print(StudentNames)
Counting sort
Counting Sort is much different from Bubble, Selection & Insertion Sorting algorithms. In this
algorithm, comparison between elements doesn’t happen. Counting sort is a sorting technique
based on keys between specific ranges. It works by counting the number of objects having
distinct key values then doing some arithmetic to calculate the position of each object in the
output sequence.
Now create another array and store value in this array by getting value from UnsortedArray and find the
position of this value in CountArray and place on same position in SortedArray, after that decreasing
position by 1 in CountArray to place same value at an index smaller than this index.
SortedArray 0 1 2 3 4 5 6
1 2 3 3 5 5 7
Practical Demonstration
Example: 4.8 (Code for value range 0 to 9)
Min = Arr[0]
Max = Arr[0]
for i in range(len(Arr)):
if (Min > Arr[i]):
Min = Arr[i]
for i in range(len(Arr)):
count[Arr[i] - Min] += 1
for i in range(len(count)):
print(count[i], end=" ")
print()
for i in range(len(count)):
print(count[i], end=" ")
print()
for i in range(len(SortedArray)):
SortedArray[count[Arr[i] - Min] - 1] = Arr[i]
count[Arr[i] - Min] -= 1
for i in range(len(SortedArray)):
print(SortedArray[i], end=" ")
for i in range(len(SortedArr)):
print(SortedArr[i], end=" ")
Lab 4 Exercises:
LAB 5
Searching Algorithms
Not even a single day pass, when we do not have to search for something in our day to day life,
car keys, books, pen, mobile charger and what not. Same is the life of a computer, there is so
much data stored in it, that whenever a user asks for some data, computer has to search its
memory to look for the data and make it available to the user.
What if you have to write a program to search a given number in an array? How will you do it?
Well, to search an element in a given array, there are two popular algorithms available:
1. Linear Search
2. Binary Search
Linear Search
Linear search is a very basic and simple search algorithm. In Linear search, we search an
element or value in a given array by traversing the array from the starting, till the desired
element or value is found.
It compares the element to be searched with all the elements present in the array and when
the element is matched successfully, it returns the index of the element in the array, else it
return -1.
Linear Search is applied on unsorted or unordered lists, when there are fewer elements in a list.
It has a time complexity of O(n), which means the time is linearly dependent on the
number of elements, which is not bad, but not that good too.
Example: 5.1
flag = False
for i in range(len(Numbers)):
if(Numbers[i] == Number):
flag = True
break
if (flag):
print("Value Found on Index: " + str(i))
else:
print("Value Not Found in Array")
Example: 5.2
Highest = Marks[0]
Lowest = Marks[0]
Example: 5.3
Students = array([
["5111", "Furqan", "Abbsi", "0324225822", "59%"],
["5112", "Umer", "Aslam", "03472990222", "64%"],
["5119", "Asim", "Hassan", "929299292", "52%"],
["5119", "Zeeshan", "Hanif", "929929111", "75%"],
["6000", "Umair", "Iqbal", "9212038201", "87%"]
])
Index = -1
for i in range(len(Students)):
if (Students[i, 0] == ID):
Index = i
break
Binary Search
Binary Search is used with sorted array or list. In binary search, we follow the following steps:
We start by comparing the element to be searched with the element in the middle of the
list/array.
If we do not get a match, we check whether the element to be searched is less or greater than
in value than the middle element.
If the element/number to be searched is greater in value than the middle number, then we pick
the elements on the right side of the middle element (as the list/array is sorted, hence on the
right, we will have all the numbers greater than the middle number), and start again from the
step 1.
If the element/number to be searched is lesser in value than the middle number, then we pick
the elements on the left side of the middle element, and start again from the step 1.
Binary Search is useful when there are large numbers of elements in an array and they are
sorted.
So a necessary condition for Binary search to work is that the list/array should be sorted.
Some Key Point about Binary Search Algorithm
Example: 5.4
if (flag):
print("Number found on Index: " + str(Mid))
else:
print("Number Not found in array")
Example: 5.5
Example: 5.6
Students = array([
["5111", "Furqan", "Abbsi", "0324225822", "59%"],
["5112", "Umer", "Aslam", "03472990222", "64%"],
["5113", "Asim", "Hassan", "929299292", "52%"],
["5114", "Zeeshan", "Hanif", "929929111", "75%"],
["5115", "Umair", "Iqbal", "9212038201", "87%"]
])
First = 0
Last = len(Students) - 1
Mid = int((First + Last) / 2)
Index = -1
Lab 5 Exercises:
LAB 6
Linked List
In a game of Treasure Hunt, you start by looking for the first clue. When you find it, instead of
having the treasure, it has the location of the next clue and so on. You keep following the clues
until you get to the treasure.
A linked list is a way to store a collection of elements. Like an array these can be character or
integers. Each element in a linked list is stored in the form of a node. A node is a collection of
two sub-elements or parts. A data part that stores a number, a string or any other type of data
and a next part that stores the link to the next node.
A linked list is formed when many such nodes are linked together to form a chain. Each node
points to the next node present in the order. The first node is always used as a reference to
traverse the list and is called HEAD. The last node points to NULL.
Example: 6.1
class Node:
def __init__(self, value):
self.Info = value
self.Next = None
def Print(self):
print(self.Info)
if(self.Next is not None):
self.Next.Print()
Start = Node(22)
Start.Next = Node(24)
Start.Next.Next = Node(25)
Start.Print()
class LinkedList:
def __init__(self):
self.Start = None
def Print(self):
if(self.Start == None):
print("List is Empty")
else:
self.Start.Print()
class LinkedList:
def __init__(self):
self.Start = None
def Count(self):
if (self.Start is None):
print("List is Empty")
else:
ptr = self.Start
count = 1
while(ptr.Next != None):
ptr = ptr.Next
count += 1
return count
if(ptr == None):
print(f"Value {value} Not found is List")
else:
print(f"Value {ptr.Info} found on Position {Count}")
if(ptr == None):
print(f"Value {Find} not found in List!")
else:
ptr.Info = value
print(f"Value {Find} has been changed with {ptr.Info}")
def DeleteFromEnd(self):
if (self.Start is None):
print("List is Empty")
elif (self.Start.Next is None):
self.Start = None
else:
ptr = self.Start;
while(ptr.Next.Next is not None):
ptr = ptr.Next
ptr.Next = None
def DeleteFromBegin(self):
if(self.Start is None):
print("List is Already Empty")
else:
self.Start = self.Start.Next
Lab 6 Exercises:
LAB 7
Two-way & Circular Linked List
Example: 7.1 (Create Node Class and DoublyLinked Class and Add Print Method in DoublyLinked class)
class Node:
class DoublyLinked:
def PrintList(self):
if(self.Start == None):
print("List is Empty")
else:
ptr = self.Start
while(ptr != None):
print(ptr.Info)
ptr = ptr.Next
def DeleteFromBegin(self):
if(self.Start == None):
print("List is Already Empty")
elif (self.Start.Next == None):
self.Start = None
else:
self.Start = self.Start.Next
self.Start.Prev = None
def DeleteFromEnd(self):
if(self.Start == None):
print("List is Already Empty")
elif(self.Start.Next == None):
self.Start = None
else:
ptr = self.Start
while(ptr.Next != None):
ptr = ptr.Next
ptr.Prev.Next = None
for singly linked list, next pointer of last item points to the first item
In doubly linked list, prev pointer of first item points to last item as well.
Example: 7.6
A three-member circular singly linked list can be created as:
class Node:
One = Node(1);
two = Node(2);
three = Node(3);
One.Next = two
two.Next = three
three.Next = One
One.Print(One)
#it will run infinite Time
Lab-7 Exercises:
LAB 8
Stack
A Stack is a list of the element in which an element may be inserted or deleted only at one end,
called the top of the stack. It is a simple data structure that allows adding and removing
elements in a particular order. Every time an element is added, it goes on the top of the stack
and the only element that can be removed is the element that is at the top of the stack.
Example: 8.1
from numpy import array
top = [-1]
MAXSTK = 5
Arr = array([0 for i in range(0, MAXSTK)])
print(Arr)
top = [-1]
MAXSTK = 5
Arr = array([0 for i in range(0, MAXSTK)])
peek() − get the top data element of the stack, without removing it.
Example: 8.3
top = [-1]
MAXSTK = 5
Arr = array([0 for i in range(0, MAXSTK)])
print(Peek(Arr, top))
1. Get TOP and MAXSTK of Stack (MAXSTK means total strength of element in Array)
2. Check the TOP is equal or not equal to MAXSTK - 1
3. If the TOP is equal to MAXSTK - 1, then print the stack is already full
Example: 8.4
def IsFull(top, MAXSTK):
if(top[0] == MAXSTK-1):
return True
else:
return False
top = [-1]
MAXSTK = 5
Arr = array([0 for i in range(0, MAXSTK)])
print(IsFull(top, MAXSTK))
Example: 8.5
def IsEmpty(top):
if(top[0] == -1):
return True
else:
return False
top = [-1]
MAXSTK = 5
Arr = array([0 for i in range(0, MAXSTK)])
print(IsEmpty(top))
Below we have a simple python program implementing stack data structure while following the
object oriented programming concepts.
Example: 8.6
from numpy import array
class Stack:
def IsFull(self):
if(self.Top == self.Size - 1):
return True
else:
return False
def IsEmpty(self):
if(self.Top == -1):
return True
else:
return False
def Pop(self):
if(self.IsEmpty()):
print("stack is Empty")
else:
Value = self.Arr[self.Top]
self.Top -= 1
return Value
def Peek(self):
if(self.Top == -1):
print("Stack is Empty")
else:
return self.Arr[self.Top]
def Len(self):
if(self.Top == -1):
print("List is Empty")
else:
return self.Top + 1
def Print(self):
if(self.Top == -1):
print("Stack is Empty")
else:
for i in range(0 , self.Top + 1):
print(self.Arr[i], end=" ")
MyStack = Stack(5)
while (True):
print();
print("1 : Push an element on the stack ");
print("2 : Pop an element from the stack ");
print("3 : Display the top element ");
print("4 : Display all elements ");
print("5 : Display size of stack ");
print("6 : Close the program ");
if (choice == 6):
break
elif(choice == 1):
print()
Item = int(input("Enter the Value to be pushed in stack: "))
MyStack.Push(Item)
elif(choice == 2):
print()
print(str(MyStack.Pop()) + " value removed from stack")
elif(choice == 3):
print()
print("Top value of stack is: " + str(MyStack.Peek()))
elif(choice == 4):
MyStack.Print()
elif(choice == 5):
print(MyStack.Len())
print("End")
Lab-8 Exercises:
LAB 9
Recursion
Recursion is a programming technique in which a method makes a call to itself to solve a
particular problem. Such methods are called recursive.
Recursion is a programming technique whose correct usage leads to elegant solutions to certain
problems. Sometimes its usage could considerably simplify the programming code and its
readability.
def PrintNumber(Num):
if Num <= 10:
print(Num, end=" ")
PrintNumber(Num + 1)
PrintNumber(0)
PrintTable(2, 1, 10)
This activation record keeps the information about local variables, formal parameters, return
address and all information passed to the caller function.
def Factorial(Number):
if(Number == 1):
return 1
return Number * Factorial(Number - 1)
Factorial(5)
Power(2, 3)
def Fib(Num):
if Num == 0 or Num == 1:
return Num
return Fib(Num - 2) + Fib(Num - 1)
Merge sort
Merge Sort is a Divide and Conquer algorithm. It divides the input array into two halves, calls
itself for the two halves, and then merges the two sorted halves. The merge() function is used
for merging two halves. The merge(arr, l, m, r) is a key process that assumes that arr[l..m] and
arr[m+1..r] are sorted and merges the two sorted sub-arrays into one
Example: 9.6
def mergeSort(arr):
if len(arr) > 1:
# Finding the mid of the array
mid = len(arr)//2
# into 2 halves
R = arr[mid:]
i = j = k = 0
def printList(arr):
for i in range(len(arr)):
print(arr[i], end=" ")
print()
# Driver Code
if __name__ == '__main__':
arr = [12, 11, 13, 5, 6, 7]
print("Given array is", end="\n")
printList(arr)
mergeSort(arr)
print("Sorted array is: ", end="\n")
printList(arr)
Lab 9 Exercises:
LAB 10
Polish Notations
The way to write arithmetic expression is known as a notation. An arithmetic expression can
be written in three different but equivalent notations, i.e., without changing the essence or
output of an expression. These notations are
Infix Notation
Prefix (Polish) Notation
Postfix (Reverse-Polish) Notation
These notations are named as how they use operator in expression. We shall learn the same
here in this chapter.
Infix Notation
We write expression in infix notation, e.g. a - b + c, where operators are used in-between operands. It is
easy for us humans to read, write, and speak in infix notation but the same does not go well with
computing devices. An algorithm to process infix notation could be difficult and costly in terms of time
and space consumption.
Prefix Notation
In this notation, operator is prefixed to operands, i.e. operator is written ahead of operands. For
example, +ab. This is equivalent to its infix notation a + b. Prefix notation is also known as Polish
Notation.
Postfix Notation
This notation style is known as Reversed Polish Notation. In this notation style, the operator is postfixed
to the operands i.e., the operator is written after the operands. For example, ab+. This is equivalent to
its infix notation a + b.
The following table briefly tries to show the difference in all three notations
Infix Notation Prefix Notation Postfix Notation
Sr.No.
1 a+b +ab ab+
2 (a + b) ∗ c ∗+abc ab+c∗
3 a ∗ (b + c) ∗a+bc abc+∗
5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗
Parsing Expressions
As we have discussed, it is not a very efficient way to design an algorithm or program to parse
infix notations. Instead, these infix notations are first converted into either postfix or prefix
notations and then computed.
To parse any arithmetic expression, we need to take care of operator precedence and
associativity also.
Precedence
When an operand is in between two different operators, which operator will take the operand
first, is decided by the precedence of an operator over others. For example −
As multiplication operation has precedence over addition, b * c will be evaluated first. A table
of operator precedence is provided later.
Associativity
Associativity describes the rule where operators with the same precedence appear in an
expression. For example, in expression a + b − c, both + and – have the same precedence, then
which part of the expression will be evaluated first, is determined by associativity of those
operators. Here, both + and − are left associative, so the expression will be evaluated as (a + b)
− c.
Precedence and associativity determines the order of evaluation of an expression. Following is
an operator precedence and associativity table (highest to lowest) –
The above table shows the default behavior of operators. At any point of time in expression
evaluation, the order can be altered by using parenthesis. For example −
In a + b*c, the expression part b*c will be evaluated first, with multiplication as precedence
over addition. We here use parenthesis for a + b to be evaluated first, like (a + b)*c.
def IsEmpty(self):
if self.Top == -1:
return True
return False
def Pop(self):
item = self.List[self.Top]
self.Top -= 1
return item
def Peek(self):
return self.List[self.Top]
while(not stack.IsEmpty()):
item = Q[CounterQ]
CounterQ += 1
if (item != '+' and item != '-' and item != '*' and item != '/' and
item != '^' and item != '(' and item != ')'):
P += item
elif (item == '('):
stack.Push(item)
College of Computing & Information Sciences 65
Course Title: Lab-10
Data Structures & Algorithms Polish Notations
def EvaluatingOfPFN(P):
stack = CreateStack(100)
for i in range(len(P)):
Val = P[i]
if (Val != "+" and Val != "-" and Val != "*" and Val != "/" and Val != "^"):
stack.Push(Val)
else:
b = int(stack.Pop())
a = int(stack.Pop())
if Val == "+":
Result = a + b
stack.Push(Result)
elif Val == "-":
Result = a - b
stack.Push(Result)
elif Val == "*":
Result = a * b
stack.Push(Result)
elif Val == "/":
Result = a / b
stack.Push(Result)
elif Val == "^":
Result = a ^ b
stack.Push(Result)
return int(stack.Peek());
Q = Polish(InfixNot)
Result = EvaluatingOfPFN(Q)
print(Q)
print(Result)
Lab-10 Exercises:
LAB 11
Queue
Queue is also 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 existing element takes place from the other end called as FRONT(also called head).
This makes queue as FIFO(First in First Out) data structure, which means that element inserted
first will be removed first.
This is exactly how queue system works in real world. If you go to a ticket counter to buy movie
tickets, and are first in the queue, then you will be the first one to get the tickets. Right? Same
is the case with Queue data structure. Data inserted first, will leave the queue first.
The process to add an element into queue is called Enqueue and the process of removal of an
element from queue is called Dequeue.
Like stack, queue is also an ordered list of elements of similar data types.
Queue is a FIFO( First in First Out ) structure.
Once a new element is inserted into the Queue, all the elements inserted before the
new element in the queue must be removed, to remove the new element.
peek( ) function is oftenly used to return the value of first element without dequeuing it
Examples of Queue
Queue, as the name suggests is used whenever we need to manage any group of objects in an
order in which the first one coming in, also gets out first while the others wait for their turn, like
in the following scenarios:
Serving requests on a single shared resource, like a printer, CPU task scheduling etc.
In real life scenario, Call Center phone systems uses Queues to hold people calling them
in an order, until a service representative is free.
Handling of interrupts in real-time systems. The interrupts are handled in the same
order as they arrive i.e First come first served.
n = 5
front = 0
rear = [-1]
queue = array([0 for i in range(n)])
enQueue(queue, n, front, rear, 22)
enQueue(queue, n, front, rear, 23)
enQueue(queue, n, front, rear, 24)
enQueue(queue, n, front, rear, 25)
enQueue(queue, n, front, rear, 26)
enQueue(queue, n, front, rear, 27)
print(queue)
n = 5
front = 0
rear = [-1];
queue = array([0 for i in range(n)])
enQueue(queue, n, front, rear, 22)
enQueue(queue, n, front, rear, 23)
enQueue(queue, n, front, rear, 24)
print(queue)
deQueue(queue, n, front, rear) #out 22
deQueue(queue, n, front, rear) #out 23
Circular Queue:
Circular queue avoids the wastage of space in a regular queue implementation using arrays.
As you can see in the above image, after a bit of enqueueing and dequeueing, the size of the
queue has been reduced.
The indexes 0 and 1 can only be used after the queue is reset when all the elements have been
dequeued.
However, the check for full queue has a new additional case:
Case 1: FRONT = 0 && REAR == SIZE - 1
Case 2: FRONT == REAR + 1
The second case happens when REAR starts from 0 due to circular increment and when
its value is just 1 less than FRONT, the queue is full.
def IsFull(self):
if ((self.rear + 1) % self.size == self.front):
return True
return False
def IsEmpty(self):
if self.front == -1:
return True
return False
self.Array[self.rear] = value
def Print(self):
if(self.IsEmpty()):
print ("Queue is Empty")
elif (self.rear >= self.front):
print("Elements in the circular queue are:", end = " ")
for i in range(self.front, self.rear + 1):
print(self.Array[i], end = " ")
else:
print ("Elements in Circular Queue are:", end = " ")
for i in range(self.front, self.size):
print(self.Array[i], end = " ")
for i in range(0, self.rear + 1):
print(self.Array[i], end = " ")
def Peek(self):
return self.Array[self.front]
def Size(self):
if(self.IsEmpty()):
return 0
else:
if(self.rear >= self.front):
size = (self.rear - self.front)
else:
size = ((self.size - self.front) + self.rear)
size += 1
return size
MyQueue = Queue(5)
while (True):
print();
print("1 : Add an element on the Queue ");
print("2 : Delete an element from the Queue ");
print("3 : Display the top element ");
print("4 : Display all elements ");
print("5 : Display size of Queue ");
print("6 : Close the program ");
if (choice == 6):
break
elif(choice == 1):
print()
Item = int(input("Enter the Value to add in Queue: "))
MyQueue.enqueue(Item)
elif(choice == 2):
print()
print(str(MyQueue.dequeue()) + " value removed from Queue")
elif(choice == 3):
print()
print("Front value of Queue is: " + str(MyQueue.Peek()))
elif(choice == 4):
MyQueue.Print()
elif(choice == 5):
print(MyQueue.Size())
print("End")
Lab-11 Exercises:
LAB 12
Binary Search Tree
A Tree is a non linear data structure containing the set of one or more data nodes where one
node is designated as the root of the tree while the remaining nodes are called as the children
of the root. The nodes other than the root node are partitioned into the non empty sets where
each one of them is to be called sub-tree. Nodes of a tree either maintain a parent-child
relationship between them or they are sister nodes. In a general tree, a node can have any
number of children nodes but it can have only a single parent.
The tree data structure can be classified into six different categories. But in this class/Lab we
only discuss Binary Search Tree.
Binary Search tree is a non linear data structure, in which the nodes are arranged in a specific
order. This is also called ordered binary tree. In a binary search tree, the value of all the nodes
in the left sub-tree is less than the value of the root. Similarly, value of all the nodes in the right
sub-tree is greater than or equal to the value of the root. This rule will be recursively applied to
all the left and right sub-trees of the root.
Try to Create a Binary Search Tree using the following value on your register
[50, 60, 55, 59, 40, 30, 20, 21, 35, 45, 47, 46]
Searching become very efficient in a binary search tree since, we get a hint at each step,
about which sub-tree contains the desired element.
The binary search tree is considered as efficient data structure in compare to arrays and
linked lists. In searching process, it removes half sub-tree at every step. Searching for an
element in a binary search tree takes O(log2n) time. In worst case, the time it takes to
search an element is 0(n).
It also speed up the insertion and deletion operations as compare to that in array and
linked list.
Example: 12.1
class TreeNode:
class BST
def __init__(self):
self.Root = None
Example: 12.2
def Insert(self, value):
if(self.Root == None):
self.Root = TreeNode(value)
else:
ptr = self.Root
while(ptr != None):
if(value < ptr.data):
if(ptr.left == None):
ptr.left = TreeNode(value)
break
else:
ptr = ptr.left
Pre-order Traversal:
In this technique, we do the following:
1. Process data of root node.
2. First, traverse left sub-tree completely.
3. Then, traverse right sub-tree.
Example: 12.3
Post-order Traversal
In this traversal technique we do the following:
Example: 12.4
In-order Traversal
In in-order traversal, we do the following:
Example: 12.5
Desc-order Traversal
In Desc-order traversal, we do the following:
Example: 12.5
Example: 13.2 (Add FindHighestValue method that return highest value of the Tree)
def FindHighestValue(self):
if self.Root is None:
print("Tree is Empty")
return None
else:
ptr = self.Root
while ptr.right is not None:
ptr = ptr.right
return ptr
Example: 13.3 (Add FindLowestValue method that return lowest value of the Tree)
def FindLowestValue(self):
if self.Root is None:
print("Tree is Empty")
return None
else:
ptr = self.Root
while ptr.left is not None:
ptr = ptr.left
return ptr
Example: 13.4
Lab-12 Exercises:
LAB 13
Graph
A graph is a pictorial representation of a set of objects where some pairs of objects are
connected by links. The interconnected objects are represented by points termed as vertices,
and the links that connect the vertices are called edges.
Formally, a graph is a pair of sets (V, E), where V is the set of vertices and E is the set of edges,
connecting the pairs of vertices.
Graphs are used to solve real-life problems that involve representation of the problem space as
a network. Examples of networks include telephone networks, circuit networks, social networks
(like LinkedIn, Facebook etc.).
For example, a single user in Facebook can be represented as a node (vertex) while their
connection with others can be represented as an edge between nodes.
Each node can be a structure that contains information like user’s id, name, gender, etc.
Well, to search an element from graph, there are two popular algorithms available:
BFS starts from a node, then it checks all the nodes at distance one from the beginning node,
then it checks all the nodes at distance two, and so on. So as to recollect the nodes to be
visited, BFS uses a queue
1. Start by putting any one of the graph’s vertices at the back of the queue.
2. Now 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 those which are not within the visited
list to the rear of the queue.
4. Keep continuing steps two and three till the queue is empty.
Example:13.1
graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}
# Driver Code
print("Following is the Breadth-First Search")
bfs(visited, graph, '5') # function calling
The Depth-First Search is a recursive algorithm that uses the concept of backtracking. It involves
thorough searches of all the nodes by going ahead if potential, else by backtracking. Here, the
word backtrack means once you are moving forward and there are not any more nodes along
the present path, you progress backward on an equivalent path to seek out nodes to traverse.
All the nodes are progressing to be visited on the current path until all the unvisited nodes are
traversed after which subsequent paths are going to be selected
1. We will start by putting any one of the graph's vertex on top of the stack.
2. After that take the top item of the stack and add it to the visited list of the vertex.
3. Next, create a list of that adjacent node of the vertex. Add the ones which aren't in the
visited list of vertexes to the top of the stack.
4. Lastly, keep repeating steps 2 and 3 until the stack is empty.
Example 13.2
# Using a Python dictionary to act as an adjacency list
graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}
# Driver Code
print("Following is the Depth-First Search")
dfs(visited, graph, '5')
Exercise 13: