0% found this document useful (0 votes)
20 views169 pages

Dsapb3 Notes

The document outlines an online training course on Data Structures and Algorithms with Python, led by K Prakash Babu, with a duration of 2 months and a fee of Rs.999. It includes a comprehensive syllabus covering various data structures, algorithms, and practical implementations, along with programming examples. Participants will have access to course materials via Google Drive for six months.

Uploaded by

Yogesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views169 pages

Dsapb3 Notes

The document outlines an online training course on Data Structures and Algorithms with Python, led by K Prakash Babu, with a duration of 2 months and a fee of Rs.999. It includes a comprehensive syllabus covering various data structures, algorithms, and practical implementations, along with programming examples. Participants will have access to course materials via Google Drive for six months.

Uploaded by

Yogesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 169

good morning everyone...

welcome to durgasoft online training....

welcome to data strctures and algorithms with python course...

K prakash Babu, 15 years of exp as trainer....

course:
-------
title -----> data structures and algorithms with python (DSAPB3)
timings ---> 11am to 12.15pm (monday to saturday)
duration --> 2 months (july and aug)
fees ------> Rs.999/-
contact ---> 9246 212143 or 7207 212427 or
[email protected]
content ---> Running Notes (GDrive) + Videos (GDrive) [6 months access]

https://www.youtube.com/watch?v=TUBPM_-yCNE&list=PLd3UqWTnYXOm5NBgfgE-
K2r8WxgTaJeUX

syllabus:
---------
01. introduction to data structures and algorithms
02. analysis of algorithms or complexities of algorithms
03. sample problems with python implementations [25 programs]
04. python inbuilt data structures (str, list, tuple, set & dict)
05. str data structure
06. programs on str data structure
07. list/array data structure
08. list/array based programs
09. recursion
10. recursion based applications
11. sorting algorithms
12. searching algorithms
13. divide and conquer algorithms
14. backtracking (N-Queens, Sudoku)
15. dynamic programming
16. greedy algorithms
17. linked list data strcture
18. stack data strcture
19. queue data strcture
20. hashtable/hashing data strcture
21. tree data strctures
22. priority queues / heaps
23. graph data strctures
24. bit manipulations

N-Queens Problem Statement:


---------------------------
n = 1, n= 2, n = 3, n = 4, ......
Factorial of the given number:
------------------------------
import math
def fun1(n):
#loops
f=1
for i in range(1,n+1):
f=f*i
return f

def fun2(n):
#recursion
if n==0:
return 1
return n*fun2(n-1)

def fun3(n):
return math.factorial(n)

for i in range(6):
print(f"{i}\t{fun1(i)}\t{fun2(i)}\t{fun3(i)}")

C:\test>py test.py
0 1 1 1
1 1 1 1
2 2 2 2
3 6 6 6
4 24 24 24
5 120 120 120

Linked List:
------------
collection of nodes is called as linked list, where every node is linked
with another node.

Node ---> single node and double node

1. single linked list


2. double linked list
3. circular single linked list
4. circular double linked list

diaplay / traversing / print list


---------------------------------
while loop
head
None

def display():
if head==None:
print("list is empty")
return
currNode = head
while currNode!=None:
print(currNode.data)
currNode = currNode.next

def insertatfirst(data):
newnode = node(data)
if head==None:
head = newnode
return
newnode.next = head
head = newnode

def insertatlast(data):
newnode = node(data)
if head==None:
head = newnode
return
currNode = head
while currNode.next!=None:
currNode = currNode.next
currNode.next = newnode

prime number application


------------------------
2 -------> 1, 2 -----> true
3 -------> 1, 3 -----> true
4 -------> 1, 2, 4 --> false
5 -------> 1, 5 -----> true
6 -------> 1, 2, 3, 6-> false

def prime1(n):
f=0
for i in range(1,n+1):
if n%i==0:
f=f+1
return f==2

def prime2(n):
for i in range(2,n):
if n%i==0:
return False
return True

def prime3(n,i):
if i==1:
return True
if n%i==0:
return False
return prime3(n,i-1)
for i in range(2,11):
print(f"{i} \t {prime1(i)} \t {prime2(i)} \t {prime3(i,i//2)}")

C:\test>py test.py
2 True True True
3 True True True
4 False False False
5 True True True
6 False False False
7 True True True
8 False False False
9 False False False
10 False False False

introduction to data structures and algorithms


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Problem --> Idea --> Algorithm --> Flow Chart --> Program --> Output
(solution)

algorithm:
----------
step by step process for solving any problem.

problem: addition of two numbers


--------------------------------
algorithm

1. read 'a' value from the user as an integer


2. read 'b' value from the user as an integer
3. logic c <--- a+b
4. print the value of c as result

flowchart:
----------
pictorial or diagrametic representation of an algorithm is called as
flowchart

implementation:
---------------
a = int(input("Enter first number: "))
b = int(input("Enter second number: "))
c = a + b
print(f"a:{a}, b:{b} and addition:{c}")

C:\test>py test.py
Enter first number: 10
Enter second number: 20
a:10, b:20 and addition:30

advantages of algorithms:
-------------------------
==> problem will be simplified.
==> easy to understand the problem statement.
==> easy to implement by using any programming language.
==> we will get a format / template / pattern for solving the problem.

properties of an algorithm:
---------------------------
1) every algorithm should take zero or more inputs.
2) every algorithm should print atleast one output.
3) deterministic (input --> output same thing should happend if u run
again)
4) instructions are very clear and unambigious
5) terminate at finate number of steps
6) efficient (logic should be clear)

types of algorithms:
--------------------
1) simple algorithms
2) math algorithms (formulas)
3) recursive algorithms
4) divide and conquer algorithms
5) back tracking algorithms
6) dynamic programming
7) greedy algorithms
8) searching and sorting methods
etc

data structure:
---------------
data structure is the concept of organzing data. data structures are
classified into two types.

1) linear data structures


2) non-linear data structures

linear data structures


----------------------
data, allocation of memory for that data is in continues manner.

Ex:
arrays/list
strings
linked lists
stack
queue
pq
hashtable etc

non-linear data structures


--------------------------
data, allocation of memory for that data is not in continues manner.

Ex:
trees
graphs
heap
etc

The following are the basic operations, that can be performed on any ds

1) inserting
2) selecting
3) deleting
4) updating
5) searching etc

array / list
~~~~~~~~~~~~~
It is a collection / group of objects.

creation
insertion
delete
select/display
update
search

string
~~~~~~
It is a collection / group of characters. (ASCII/UNICODE)

create
insert
display/select
update
search
delete

linked list
~~~~~~~~~~~
It is also collection of objects, but those objects containes some extra
pointers (single node, double node).

single linked list


double linked list
circular single linked list
circular double linked list

insert
delete
display
update
search etc

stack data structure:


~~~~~~~~~~~~~~~~~~~~~
it is also collection of objects, but it follows LIFO method.

LIFO ---> Last In First Out


push (insert)
pop (delete)
peek
display
search etc

queue data structure:


~~~~~~~~~~~~~~~~~~~~~
it is also collection of objects, but it follows FIFO method.

FIFO ---> First In First Out

insert
delete
update
display
search etc

tree data structure:


~~~~~~~~~~~~~~~~~~~~
it is also collection of objects or nodes

TREE, BINARY TREE, BINARY SEARCH TREE, AVL TREES

insert
deletion
search
applications etc

graph data structure:


~~~~~~~~~~~~~~~~~~~~~
it is also collection of objects or nodes with some connection (edges)

graph, bfs, dfs, shortest path...

insert
delete
search
applications etc

approaches to solve any problem:


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Theoritical knowledge is essential but it is insufficient to solve any
problem the following are the main approaches to solve any problem in the
real world.

1) constraints (conditions)
2) idea generation
3) complexity analysis
4) coding
5) testing

1) constraints (conditions)
---------------------------
Given problem constraints are very very imp for solving any problem.
first we have to identify all the constraints related to the given
problem.

Ex:
sorting the data in order

-> asc order or desc order


-> number of elements
-> type of element etc

2) idea generation
------------------
* more if you practice, you will get an idea.
* by practicing you will get a pattern or template of the program.
* easily we can solve unseen problem.

1. try to simplify unseen problem at hand (paper and pen).


2. few examples
3. think about suitable data structure if need.
4. think about similar problem that already we solved.

3) complexity analysis
----------------------
=> finding the solution for a proble is not good.
=> finding the solution which executes very fastly and takes less memory.
=> calculate time and space complexities of alg and go for impl.

4) coding
----------
=> if you have all the data, then we can write code.
=> select any programming langauge. (python)
=> select proper IDE
=> and try to write modular code (functions/methods) (reusability)

5) testing
----------
=> after completion of program, we have to supply some data and check
whether
the code is working or not.
=> pply varies main test cases and also corner test cases.

Ex:
A = [1, 2, 3, 4, 5]
0 ---> 1
100 -> ?

Chapter 02: Sample Algorithms and Implementation in Python


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01. Check wehther the given number is even number or odd number?
----------------------------------------------------------------

def fun1(n):
if n%2==0:
return True
else:
return False

def fun2(n):
return n%2==0

def fun3(n):
return (n&1)==0

n=int(input("Enter any number: "))


print(fun1(n))
print(fun2(n))
print(fun3(n))

C:\test>py test.py
Enter any number: 5
False
False
False

C:\test>py test.py
Enter any number: 6
True
True
True

02. Max of two numbers


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

def fun1(n1,n2):
if n1>n2:
return n1
else:
return n2

def fun2(n1,n2):
return n1 if n1>n2 else n2

def fun3(n1,n2):
return max(n1,n2)

n1=int(input("Enter any number: "))


n2=int(input("Enter any number: "))
print(fun1(n1,n2))
print(fun2(n1,n2))
print(fun3(n1,n2))

C:\test>py test.py
Enter any number: 1
Enter any number: 2
2
2
2

C:\test>py test.py
Enter any number: 1
Enter any number: -2
1
1
1

03. Max/ Min of three numbers


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

def fun1(n1,n2,n3):
if n1>n2 and n1>n3:
return n1
elif n2>n3:
return n2
else:
return n3

def fun2(n1,n2,n3):
return n1 if n1>n2 and n1>n3 else n2 if n2>n3 else n3

def fun3(n1,n2,n3):
return max(n1,n2,n3)

n1=int(input("Enter any number: "))


n2=int(input("Enter any number: "))
n3=int(input("Enter any number: "))

print(fun1(n1,n2,n3))
print(fun2(n1,n2,n3))
print(fun3(n1,n2,n3))

C:\test>py test.py
Enter any number: 1
Enter any number: 2
Enter any number: 3
3
3
3

C:\test>py test.py
Enter any number: 1
Enter any number: 2
Enter any number: -3
2
2
2

C:\test>py test.py
Enter any number: 1
Enter any number: -2
Enter any number: -3
1
1
1

04. Max/Min of four numbers


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

def fun1(n1,n2,n3,n4):
if n1>n2 and n1>n3 and n1>n4:
return n1
elif n2>n3 and n2>n4:
return n2
elif n3>n4:
return n3
else:
return n4

def fun2(n1,n2,n3,n4):
return n1 if n1>n2 and n1>n3 and n1>n4 else n2 if n2>n3 and n2>n4
else n3 if n3>n4 else n4

def fun3(n1,n2,n3,n4):
return max(n1,n2,n3,n4)

n1=int(input("Enter any number: "))


n2=int(input("Enter any number: "))
n3=int(input("Enter any number: "))
n4=int(input("Enter any number: "))

print(fun1(n1,n2,n3,n4))
print(fun2(n1,n2,n3,n4))
print(fun3(n1,n2,n3,n4))

C:\test>py test.py
Enter any number: 1
Enter any number: 2
Enter any number: 3
Enter any number: 4
4
4
4

C:\test>py test.py
Enter any number: 1
Enter any number: 2
Enter any number: 3
Enter any number: -4
3
3
3

C:\test>py test.py
Enter any number: 1
Enter any number: 2
Enter any number: -3
Enter any number: -4
2
2
2

C:\test>py test.py
Enter any number: 1
Enter any number: -2
Enter any number: -3
Enter any number: -4
1
1
1

05. swaping of two numbers


--------------------------
def swap_version1(a,b):
print(f"Before swaping => a: {a} and b: {b}")
t = a
a = b
b = t
print(f"After swaping => a: {a} and b: {b}")

def swap_version2(a,b):
print(f"Before swaping => a: {a} and b: {b}")
a,b = b,a
print(f"After swaping => a: {a} and b: {b}")

def swap_version3(a,b):
print(f"Before swaping => a: {a} and b: {b}")
a = a + b
b = a - b
a = a - b
print(f"After swaping => a: {a} and b: {b}")

def swap_version4(a,b):
print(f"Before swaping => a: {a} and b: {b}")
a = a * b
b = a // b
a = a // b
print(f"After swaping => a: {a} and b: {b}")

def swap_version5(a,b):
print(f"Before swaping => a: {a} and b: {b}")
a = a ^ b
b = a ^ b
a = a ^ b
print(f"After swaping => a: {a} and b: {b}")

a=int(input("Enter first number: "))


b=int(input("Enter second number: "))

swap_version1(a,b)
swap_version2(a,b)
swap_version3(a,b)
swap_version4(a,b)
swap_version5(a,b)

C:\test>py test.py
Enter first number: 1
Enter second number: 2
Before swaping => a: 1 and b: 2
After swaping => a: 2 and b: 1
Before swaping => a: 1 and b: 2
After swaping => a: 2 and b: 1
Before swaping => a: 1 and b: 2
After swaping => a: 2 and b: 1
Before swaping => a: 1 and b: 2
After swaping => a: 2 and b: 1
Before swaping => a: 1 and b: 2
After swaping => a: 2 and b: 1

06. Absolute value of the given number


--------------------------------------
def fun1(n):
if n<0:
return -n
return n

def fun2(n):
return n if n>0 else -n

def fun3(n):
return abs(n)

n = int(input("Enter n value: "))


print(f"num: {n} and abs value: {fun1(n)}")
print(f"num: {n} and abs value: {fun2(n)}")
print(f"num: {n} and abs value: {fun3(n)}")

C:\test>py test.py
Enter n value: 10
num: 10 and abs value: 10
num: 10 and abs value: 10
num: 10 and abs value: 10

C:\test>py test.py
Enter n value: -10
num: -10 and abs value: 10
num: -10 and abs value: 10
num: -10 and abs value: 10

07. Sum of n natural numbers


----------------------------
def fun1(n):
s=0
for i in range(1,n+1):
s=s+i
return s

def fun2(n):
return (n*(n+1))//2

def fun3(n):
if n==0:
return 0
return n+fun3(n-1)

n = int(input("Enter n value: "))


print(f"num: {n} and sum of {n} natural nums: {fun1(n)}")
print(f"num: {n} and sum of {n} natural nums: {fun2(n)}")
print(f"num: {n} and sum of {n} natural nums: {fun3(n)}")

C:\test>py test.py
Enter n value: 5
num: 5 and sum of 5 natural nums: 15
num: 5 and sum of 5 natural nums: 15
num: 5 and sum of 5 natural nums: 15

C:\test>py test.py
Enter n value: 0
num: 0 and sum of 0 natural nums: 0
num: 0 and sum of 0 natural nums: 0
num: 0 and sum of 0 natural nums: 0

C:\test>py test.py
Enter n value: 10
num: 10 and sum of 10 natural nums: 55
num: 10 and sum of 10 natural nums: 55
num: 10 and sum of 10 natural nums: 55

08. Factorial of the given number


---------------------------------
import math

def fun1(n):
f=1
for i in range(1,n+1):
f=f*i
return f

def fun2(n):
if n==0:
return 1
return n*fun2(n-1)

def fun3(n):
return math.factorial(n)
n = int(input("Enter n value: "))
print(f"num: {n} and factorial: {fun1(n)}")
print(f"num: {n} and factorial: {fun2(n)}")
print(f"num: {n} and factorial: {fun3(n)}")

C:\test>py test.py
Enter n value: 5
num: 5 and factorial: 120
num: 5 and factorial: 120
num: 5 and factorial: 120

C:\test>py test.py
Enter n value: 0
num: 0 and factorial: 1
num: 0 and factorial: 1
num: 0 and factorial: 1

C:\test>py test.py
Enter n value: 3
num: 3 and factorial: 6
num: 3 and factorial: 6
num: 3 and factorial: 6

09. Extracting digits from the given number


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

n = int(input("Enter n value: "))

while n!=0:
d = n % 10
print(d)
n = n // 10

C:\test>py test.py
Enter n value: 123
3
2
1

C:\test>py test.py
Enter n value: 1278
8
7
2
1

C:\test>py test.py
Enter n value: 121
1
2
1
10. count number of digits in the given number
----------------------------------------------
n = int(input("Enter n value: "))
count = 0
while n!=0:
count=count+1
n = n // 10

print(f"Number of digits: {count}")

C:\test>py test.py
Enter n value: 1234
Number of digits: 4

C:\test>py test.py
Enter n value: 555
Number of digits: 3

11. reverse of the given number


-------------------------------
n = int(input("Enter n value: "))
rev = 0
while n!=0:
d = n % 10
rev = rev * 10 + d
n = n // 10

print(f"Reverse: {rev}")

C:\test>py test.py
Enter n value: 1234
Reverse: 4321

C:\test>py test.py
Enter n value: 789
Reverse: 987

C:\test>py test.py
Enter n value: 999
Reverse: 999

12. paliandrome num or not


--------------------------
123 -----> 321 No
121 -----> 121 Yes

n = int(input("Enter n value: "))


temp = n
rev = 0
while n!=0:
d = n % 10
rev = rev * 10 + d
n = n // 10

print("Yes" if rev==temp else "No")

C:\test>py test.py
Enter n value: 123
No

C:\test>py test.py
Enter n value: 121
Yes

C:\test>py test.py
Enter n value: 789
No

C:\test>py test.py
Enter n value: 999
Yes

13. Trailing zeros in factorial


-------------------------------
import math
n = int(input("Enter n value: "))
f = math.factorial(n)
t = f
c = 0

while t!=0:
if t%10!=0:
break
c=c+1
t=t//10

print(f"num: {n}, fact: {f} and trailing zeros: {c}")

C:\test>py test.py
Enter n value: 5
num: 5, fact: 120 and trailing zeros: 1

C:\test>py test.py
Enter n value: 6
num: 6, fact: 720 and trailing zeros: 1

C:\test>py test.py
Enter n value: 7
num: 7, fact: 5040 and trailing zeros: 1

C:\test>py test.py
Enter n value: 4
num: 4, fact: 24 and trailing zeros: 0

C:\test>py test.py
Enter n value: 10
num: 10, fact: 3628800 and trailing zeros: 2

14. x to the power of y


------------------------
import math
def fun1(x,y):
m=1
for i in range(y):
m=m*x
return m

def fun2(x,y):
return int(math.pow(x,y))

def fun3(x,y):
return x**y

x = int(input())
y = int(input())
print(fun1(x,y))
print(fun2(x,y))
print(fun3(x,y))

C:\test>py test.py
2
3
8
8
8

C:\test>py test.py
5
10
9765625
9765625
9765625

15. sum of digits in the given number


-------------------------------------
12345 ---> 1+2+3+4+5 = 15

n = int(input("Enter any num: "))


s=0
while n!=0:
d=n%10
s=s+d
n=n//10
print(s)

C:\test>py test.py
Enter any num: 12345
15

C:\test>py test.py
Enter any num: 0
0

C:\test>py test.py
Enter any num: 222
6

C:\test>py test.py
Enter any num: 333
9

16. sum of even digits in the given number


------------------------------------------
n = int(input("Enter any num: "))
s=0
while n!=0:
d=n%10
if d%2==0:
s=s+d
n=n//10
print(s)

C:\test>py test.py
Enter any num: 12345
6

C:\test>py test.py
Enter any num: 0
0

C:\test>py test.py
Enter any num: 222
6

C:\test>py test.py
Enter any num: 333
0

17. sum of odd digits present in the given number


-------------------------------------------------
n = int(input("Enter any num: "))
s=0
while n!=0:
d=n%10
if d%2!=0:
s=s+d
n=n//10
print(s)

C:\test>py test.py
Enter any num: 12345
9

C:\test>py test.py
Enter any num: 0
0

C:\test>py test.py
Enter any num: 222
0

C:\test>py test.py
Enter any num: 333
9

18. sum of prime digits


-----------------------
n = int(input("Enter any num: "))
s=0
while n!=0:
d=n%10
if d in (2,3,5,7):
s=s+d
n=n//10
print(s)

C:\test>py test.py
Enter any num: 12345
10

C:\test>py test.py
Enter any num: 222
6

C:\test>py test.py
Enter any num: 666
0

19. prime number application


----------------------------
def prime1(n):
f=0
for i in range(1,n+1):
if n%i==0:
f=f+1
return f==2

def prime2(n):
for i in range(2,n):
if n%i==0:
return False
return True

for i in range(2,21):
print(f"{i} \t {prime1(i)} \t {prime2(i)}")

C:\test>py test.py
2 True True
3 True True
4 False False
5 True True
6 False False
7 True True
8 False False
9 False False
10 False False
11 True True
12 False False
13 True True
14 False False
15 False False
16 False False
17 True True
18 False False
19 True True
20 False False

20. All divisors of N


---------------------
n = int(input("Enter any number: "))

L=[]
for i in range(1,n+1):
if n%i==0:
L.append(i)
print(L)

C:\test>py test.py
Enter any number: 10
[1, 2, 5, 10]

C:\test>py test.py
Enter any number: 11
[1, 11]

21. Perfect Number


------------------
4 ---> 1, 2 ---> 1+2 = 3 No
6 ---> 1, 2, 3 --> 1+2+3 = 6 Yes

Ex:
---
n = int(input("Enter any number: "))

s=0
for i in range(1,n):
if n%i==0:
s=s+i
print("Yes" if s==n else "No")

C:\test>py test.py
Enter any number: 4
No

C:\test>py test.py
Enter any number: 5
No

C:\test>py test.py
Enter any number: 6
Yes

C:\test>py test.py
Enter any number: 24
No

C:\test>py test.py
Enter any number: 18
No

C:\test>py test.py
Enter any number: 28
Yes

22. Armstrong Number Application


--------------------------------
123 ----> 1^3 + 2^3 + 3^3 = 1 + 8 + 27 = 36 ---> No
153 ----> 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153-> Yes

n = int(input("Enter any number: "))


temp = n
s = 0
while n!=0:
d = n % 10
s = s + d**3
n = n // 10

if temp==s:
print(f"Yes {temp} is armstrong number")
else:
print(f"No {temp} is not armstrong number")

C:\test>py test.py
Enter any number: 123
No 123 is not armstrong number

C:\test>py test.py
Enter any number: 153
Yes 153 is armstrong number

23. strong number or not


------------------------
123 ---> 1! + 2! + 3! = 1 + 2 + 6 = 9 ----> No
145 ---> 1! + 4! + 5! = 1 + 24 + 120 = 145 --> Yes

import math
n = int(input("Enter any number: "))
temp = n
s = 0
while n!=0:
d = n % 10
s = s + math.factorial(d)
n = n // 10

if temp==s:
print(f"Yes {temp} is strong number")
else:
print(f"No {temp} is not strong number")

C:\test>py test.py
Enter any number: 123
No 123 is not strong number

C:\test>py test.py
Enter any number: 153
No 153 is not strong number

C:\test>py test.py
Enter any number: 145
Yes 145 is strong number

24. Fibanocci sequence


----------------------
0 1 1 2 3 5 8 13 ....

d1 = -1
d2 = 1
L = []
n = int(input("Enter how many numbers do you want?"))
for i in range(n):
d3 = d1 + d2
L.append(d3)
d1 = d2
d2 = d3
print(L)

C:\test>py test.py
Enter how many numbers do you want?10
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

25. Triboncci sequence


-----------------------
0 1 2 3 6 11 ....

d1 = -1
d2 = 0
d3 = 1
L = []
n = int(input("Enter how many numbers do you want?"))
for i in range(n):
d4 = d1 + d2 + d3
L.append(d4)
d1 = d2
d2 = d3
d3 = d4
print(L)

C:\test>py test.py
Enter how many numbers do you want?5
[0, 1, 2, 3, 6]

C:\test>py test.py
Enter how many numbers do you want?10
[0, 1, 2, 3, 6, 11, 20, 37, 68, 125]

analysis of algorithms or complexities of algorithms


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
complexity of an algorithm is the amount of time and space required to
complete its execution.

Time Complexity T(n) ----> amount of time taken by alg


Space Complexity S(n) ---> amount of space taken by alg

Note: Time ----> Seconds, min, milli sec, hours etc


Note: Space ---> bits, bytes, kb, mb, gb, tb etc

swaping of two numbers


----------------------
a,b ------> 0 unit
a,b,t ----> 1 unit

various cases in complexity calculation:


----------------------------------------
worst case complexity ---> max steps req by an algorithm
best case complexity ----> min steps req by an algorithm
avg case complexity -----> avg steps req by an algorithm

Ex:
---
100 student records

search for "Kiran"

case1: record "Kiran" is existed at 1st location -----> best


case2: record "Kiran" is existed at last location ----> worst
case3: record "Kiran" is existed at middle location --> avg

we will focus manily on "WORST CASE COMPLEXITY".

my friend taken Rs.10000/-

1st Aug ----> best


31st Aug ---> worst (dead line)
15th Aug ---> avg

Aysmptotic Noataions or Aysmptotic Analysis


-------------------------------------------
calculating running time and space of any algorithm in mathmatical units
of computation is known as asympototic notations.

1) Big Oh Notation (O)


2) Omega Notation (w)
3) Theta Notation (0)

O -----> worst case


w -----> best case
0 -----> avg case

Ex: linear search algorithm


1 to 100 search for key element

1 ------> best ---> W(1)


100 ----> worst --> O(n)
50 -----> avg ----> 0(n/2)

growth of functions:
--------------------
1) constant time O(1)
---------------------
algorithm will return a constant time.

Ex:
Access Nth element from a list/array
push/pop operation in stack
insert/delete operation in queue
access element from hash table
etc

2) linear time O(n)


-------------------
execution time of an algorithm is depends on input size (directly
proportional to input size).

Ex:
linear search
finding max element in list/array
finding min element in list/array
travesal of a tree (in,pre,post,level)
display
etc

3) logarithm time O(logn)


-------------------------
algorithm will in logarithm time. if the execution time of an algorithm
is propotional to logarithm of input size.

Ex:
binary search
inserting an element in a tree

4) Quadratic time O(n2)


-----------------------
algorithm will run time O(n2) time, if the exe time of an algorithm is
proportional to the square of input size.

Ex:
bubble sort
insertion sort
selection sort
etc

5) O(nlogn)
-----------
Algorithm will in n*logn, if the exeuction time of an alg is proportional
to product of input size and logarithm time of input size.

Ex:
---
merge sort
quick sort
heap sort
all divide and conquer based applications
etc

6) Exponential time O(2^n)


---------------------------
In these algorithms, all possible sub sets of elements of input data are
generated.

Ex: subsets, powersets etc

ab ---> "",a,b,ab
abc --> "",a,b,c,ab,ac,bc,abc

7) Factorial time O(n!)


-----------------------
All possible permutations/combinations are generated

Ex: combinations

abc ----> abc, acb, bac, bca, cab, cba

O(1)
O(n)
O(n2)
O(2^n)
O(logn)
O(nlogn)
O(n!) etc

Ex:
---
def fun(n):
c=0
i=0
while i<n:
c=c+1
i=i+1
return c

print("N=100, num of instructions O(n):",fun(100))

C:\test>py test.py
N=100, num of instructions O(n): 100

Ex:
---
def fun(n):
c=0
i=0
while i<n:
j=0
while j<n:
c=c+1
j=j+1
i=i+1
return c

print("N=100, num of instructions O(n^2):",fun(100))

C:\test>py test.py
N=100, num of instructions O(n^2): 10000

Ex:
---
def fun(n):
c=0
i=0
while i<n:
j=0
while j<n:
k = 0
while k<n:
c=c+1
k=k+1
j=j+1
i=i+1
return c

print("N=100, num of instructions O(n^3):",fun(100))

C:\test>py test.py
N=100, num of instructions O(n^3): 1000000

Ex:
---
def fun(n):
c=0
i=1
while i<n:
c=c+1
i=i*2
return c

print("N=100, num of instructions O(logn):",fun(100))

C:\test>py test.py
N=100, num of instructions O(logn): 7

Ex:
---
def fun(n):
c=0
i=n
while i>=1:
c=c+1
print(i)
i=i//2
return c

print("N=100, num of instructions O(logn):",fun(100))

C:\test>py test.py
100
50
25
12
6
3
1
N=100, num of instructions O(logn): 7

Python's Inbuilt data structures


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01. mutable objects & immutable objects
02. string data structure
03. list data structure
04. tuple data structure
05. set data structure
06. dict data structure

mutable objects & immutable objects


-----------------------------------
Once if an object is created, if we are trying to perform modifications
on that existing objects, those modifications will be reflected on the
same object, no new object will be created, such type of objects are
called as mutable objects. i.e. modifications are allowed.
Ex: list, set & dict

L = [10, 11, 12]


print(L)
print(id(L))
L.append(13)
print(L)
print(id(L))

C:\test>py test.py
[10, 11, 12]
2457825071232
[10, 11, 12, 13]
2457825071232

Once if an object is created, if we are trying to perform modifications


on that existing objects, with those modifications new obj will be
created, in the old obj modifications wn't be reflected , such type of
objects are called as immutable objects. i.e. modifications are not
allowed.

Ex: str & tuple

Ex:
---
s = "PRAKASH"
print(s) #PRAKASH
ts=s.lower()
print(ts) #prakash
print(s) #PRAKASH

C:\test>py test.py
PRAKASH
prakash
PRAKASH

string data structure


~~~~~~~~~~~~~~~~~~~~~
introduction:
-------------
==> collection/sequence/group of characters is called as string.
==> <class 'str'>
==> we can represent strings in the following forms.

1. single quotes
2. double quotes
3. triple single quotes
4. triple double quotes

Ex:
---
s1 = 'Hai'
s2 = "Hi"
s3 = '''Bye'''
s4 = """Bi"""

print(s1,type(s1))
print(s2,type(s2))
print(s3,type(s3))
print(s4,type(s4))

C:\test>py test.py
Hai <class 'str'>
Hi <class 'str'>
Bye <class 'str'>
Bi <class 'str'>

index concept
-------------
we can use index concept and subscript combination to extract individual
characters from a string.

syntax:
s[index_value]

1) index value is always integer value.


2) it can be both +ve and -ve integers.
3) +ve, it moves from left to right direction.
4) -ve, it moves from right to left direction.

Ex:
---
s = "abc"
# 012
# 321
print(s) #abc
print(s[0]) #a
print(s[1]) #b
print(s[2]) #c
print(s[-1]) #c
print(s[-2]) #b
print(s[-3]) #a

Ex:
---
s = "abc"
# 012
# 321
print(s) #abc
print(s[0]) #a
print(s[1]) #b
print(s[2]) #c
print(s[5]) #IndexError

IndexError: string index out of range


accessing string objects:
-------------------------
The following are the various methods to access the content of string

1) directly we can accesss


2) index concept
3) slice operator
4) while loop
5) for each loop

Ex:
---
s = "python"

#1st directly
print(s) #python

Ex:
---
s = "python"
# 012345

#index
print(s[0]) #p
print(s[1]) #y
print(s[2]) #t
print(s[3]) #h
print(s[4]) #o
print(s[5]) #n

Ex:
---
s = "python"
# 012345

#slice operator
#s[s:s:s]
#s --> start value
#s --> stop value
#s --> step value

print(s[0:6:1]) #python
print(s[0:6:2]) #pto
print(s[0:6:3]) #ph

Ex:
---
s = "abcdefg"
# 0123456

print(s) #abcdefg
print(s[0:7:1]) #abcdefg
print(s[0:7:2]) #aceg
print(s[0:7:3]) #adg
print(s[0:7:4]) #ae
print(s[2:5:1]) #cde
print(s[4:5:1]) #e

Note:
-----
1. the default value for start is 0
2. the default value for stop is length-1
3. the default value for step is 1

Ex:
---
s = "abcdefg"
# 0123456

print(s) #abcdefg
print(s[0:7:1]) #abcdefg
print(s[:7:1]) #abcdefg
print(s[0::1]) #abcdefg
print(s[0:7:]) #abcdefg
print(s[::])#abcdefg

Ex:
---
s = "abcdefg"
# +0123456
# -7654321
print(s) #abcdefg
print(s[-1:-8:-1]) #gfedcba
print(s[-3:-6:-1]) #edc
print(s[-1:-8:-2]) #geca
print(s[-1:-8:1]) #no output
print(s[5:2:1]) #no output
print(s[5:2:-1]) #fed

Note:
-----
1. the default value for start is -1
2. the default value for stop is -(length+1)
3. there is no default value for step

Ex:
---
s = "abcdefg"
# +0123456
# -7654321
print(s) #abcdefg
print(s[-1:-8:-1]) #gfedcba
print(s[:-8:-1]) #gfedcba
print(s[-1::-1]) #gfedcba
print(s[-1:-8:]) #no output
print(s[::]) #abcdefg
print(s[::-1]) #gfedcba

C:\test>py test.py
abcdefg
gfedcba
gfedcba
gfedcba

abcdefg
gfedcba

Ex:
---
s = "python"

#while loop
print(s) #python

index = 0
while index<len(s):
print(s[index])
index=index+1

C:\test>py test.py
python
p
y
t
h
o
n

Ex:
---
s = "python"

#for each loop


print(s) #python

for ch in s:
print(ch)

C:\test>py test.py
python
p
y
t
h
o
n

operators on string objects:


----------------------------
+ string concatenation purpose
* string repeatetion operator
in membership operator
not in membership operator
< comparing
> comparing
<= comparing
>= comparing
== comparing
!= comparing

Ex:
---
s1 = "abc"
s2 = "def"
print(s1+s2)

C:\test>py test.py
abcdef

Ex:
---
s = "abc"
print(s*3) #abcabcabc
print(2*s) #abcabc

C:\test>py test.py
abcabcabc
abcabc

Ex:
---
s = "abc"

print('a' in s) #True
print('f' in s) #False
print('w' not in s) #True
print('b' not in s) #False

C:\test>py test.py
True
False
True
False

Ex:
---
print("abc" < "mno") #True
print("pqr" < "abc") #False
print("abc" < "abc") #False
print("abc" <= "abc") #True
print("xyz" > "abc") #True
print("abc" > "abc") #False
print("abc" >= "abc") #True
print("abc" == "ijk") #False
print("abc" != "ijk") #True

common functions on string objects


----------------------------------
len(s) length of the given string
max(s) max char in string
min(s) min char in string
sorted(s) list with all char in asc order
sorted(s,reverse=True) list with all char in desc order
ord(char) returns corresponding ascii value
chr(ascii) returns char for the given ascii code

Ex:
---
s = "prakash"

print(s) #prakash
print(len(s)) #7
print(max(s)) #s
print(min(s)) #a
print(sorted(s)) #['a','a','h','k','p','r','s']
print(sorted(s,reverse=True)) #['s','r','p','k','h','a','a']

Ex:
---
print(ord('a')) #97
print(ord('A')) #65
print(ord('0')) #48

Ex:
---
print(chr(97)) #a
print(chr(98)) #b
print(chr(65)) #A
print(chr(66)) #B
print(chr(48)) #0
print(chr(49)) #1

string specific methods:


------------------------
upper() converts str into upper case
lower() converts str into lower case
swapcase() converts str from L to U and U to L
title() each word first char will be converted into upper
case
capitalize() sentence first char will be converted into upper case

Ex:
---
s = "welcome TO pYtHoN PROGRamming"

print(s) #welcome To pYtHoN PROGRamming


print(s.upper()) #WELCOME TO PYTHON PROGRAMMING
print(s.lower()) #welcome to python programming
print(s.swapcase()) #WELCOME to PyThOn progrAMMING
print(s.title()) #Welcome To Python Programming
print(s.capitalize()) #Welcome to python programming

count(str) ----> it returns num of occurrence of given sub-string


-----------------------------------------------------------------
s = "abcdabcaba"

print(s.count("a")) #4
print(s.count("ab")) #3
print(s.count("abc")) #2
print(s.count("pqr")) #0

replace(old,new) ---> it replaces old with new


----------------------------------------------
s = "abcdabcaba"

print(s.replace("abc","pqr")) #pqrdpqraba

startswith(str) ----> True if main string starts with another str


endswith(str) ------> True if main string ends with another str
------------------------------------------------------------------
s = "python is very easy"

print(s.startswith("java")) #False
print(s.startswith("python")) #True
print(s.endswith("easy")) #True
print(s.endswith("difficult")) #False

index(str) -----> it returns index of the given sub-string if not error


-----------------------------------------------------------------------
s = "python is very easy"

print(s.index("is")) #7
print(s.index("was")) #Error

C:\test>py test.py
7
ValueError: substring not found

find(str) -----> it returns index of the given sub-string if not -1


-----------------------------------------------------------------------
s = "python is very easy"

print(s.find("is")) #7
print(s.find("was")) #-1

split(delimiter) ---> it aplits the given string based on delimiter


-------------------------------------------------------------------
s = "python is very easy"

print(s.split(" ")) #['python', 'is', 'very', 'easy']


s = "21-07-2023"
print(s.split("-")) #['21','07','2023']

sep.join(list) ---> it takes sep and joins each eleme with sep
--------------------------------------------------------------
L=['python', 'is', 'very', 'easy']

print(' '.join(L)) #python is very easy


print('-'.join(L)) #python-is-very-easy
print(':'.join(L)) #python:is:very:easy

isalpha() ---> returns True if str contains only alphabets


----------------------------------------------------------
print("abc".isalpha()) #True
print("123".isalpha()) #False
print("a12".isalpha()) #False
print("$%^".isalpha()) #False

isdigit() ---> return True if str contains only digits


-------------------------------------------------------
print("abc".isdigit()) #False
print("123".isdigit()) #True
print("a12".isdigit()) #False
print("$%^".isdigit()) #False

isalnum() ---> returns True if str contains either alpha or num


---------------------------------------------------------------
print("abc".isalnum()) #True
print("123".isalnum()) #True
print("a12".isalnum()) #True
print("$%^".isalnum()) #False

03. list data structure


-----------------------
introduction to list data structure
-----------------------------------
==> it is a group of objects of differenct types i.e. hetrogeneous
==> it is represented by using []
==> index concept is allowed
==> insertion order is preserved
==> duplicates are allowed
==> it growable in nature. (add/remove/update)
==> it is mutable object i.e. modifications are allowed

Ex:
---
L = [10, 23.556, True, 1+4j, "abc"]

print(L)
print(type(L))

C:\test>py test.py
[10, 23.556, True, (1+4j), 'abc']
<class 'list'>

creation of list objects


------------------------
1) []
2) [obj1, obj2, obj3, obj4, ....]
3) list()
4) split()
5) input() with list()
6) eval()

access list objects


-------------------
1) directly
2) index concept
3) slice operator
4) while loop
5) for each loop

operators applicable on list objects


------------------------------------
+ concatenation
* repeatetion
in membership checking
not in membership checking
< comparing
> do
<= do
>= do
== do
!= do

Ex:
---
L1 = [10,20,30]
L2 = [40,50]
print(L1+L2) #[10,20,30,40,50]

Ex:
---
L = [10,20,30]
print(L*3) #[10,20,30,10,20,30,10,20,30]
print(2*L) #[10,20,30,10,20,30]

Ex:
---
L = [10,20,30]
print(10 in L) #True
print(100 in L) #False
print(20 not in L) #False
print(200 not in L) #True

Ex:
---
L1 = [10,20,30]
L2 = [10,40,50]

print(L1==L2) #False
print(L1!=L2) #True
print(L1<L2) #True
print(L1>L2) #False
print(L1<=L2) #True
print(L1>=L2) #False

nested list:
------------
a list obj within another list obj is called as nested list

Ex:
---
L = [10, 20, 30, [40, 50, 60, 70], 80, 90, 100]
# 0 1 2 3 4 5 6

print(L) #[10, 20, 30, [40, 50, 60, 70], 80, 90, 100]
print(L[0]) #10
print(L[1]) #20
print(L[2]) #30
print(L[3]) #[40, 50, 60, 70]
print(L[3][0]) #40
print(L[3][1]) #50
print(L[3][2]) #60
print(L[3][3]) #70
print(L[4]) #80
print(L[5]) #90
print(L[6]) #100

list aliasing:
--------------
providing alternative name for the existing list obj is called as list
aliasing

Ex:
---
L1 = [10, 20, 30]
L2 = L1

print(L1) #[10,20,30]
print(L2) #[10,20,30]
print(L1 is L2) #True

Ex:
---
L1 = [10, 20, 30]
L2 = L1

print(L1) #[10,20,30]
print(L2) #[10,20,30]
L1[1] = 888
print(L1) #[10,888,30]
print(L2) #[10,888,30]

cloning:
--------
it is used to create duplicate copy of list object

Ex:
---
L1 = [10, 20, 30]
L2 = L1.copy()

print(L1) #[10,20,30]
print(L2) #[10,20,30]
print(L1 is L2) #False

Ex:
---
L1 = [10, 20, 30]
L2 = L1.copy()

print(L1) #[10,20,30]
print(L2) #[10,20,30]
L1[1] = 888
print(L1) #[10,888,30]
print(L2) #[10,20,30]

list comprehension:
-------------------
easiest way to create list objects.

[]
append items or insert the items

syntax: [expr for i in sequence]


syntax: [expr for i in sequence if condition]

Ex: increment each element present in the list


----------------------------------------------
L1 = [1, 2, 3, 4, 5]
L2 = [i+1 for i in L1]
print(L1) #[1, 2, 3, 4, 5]
print(L2) #[2, 3, 4, 5, 6]

Ex: find factorial of each element in the list


----------------------------------------------
import math
L1 = [1, 2, 3, 4, 5]
L2 = [math.factorial(i) for i in L1]
print(L1) #[1, 2, 3, 4, 5]
print(L2) #[1, 2, 6, 24, 120]

Ex: convert each name present in the list into upper case
---------------------------------------------------------
L1 = ["Prakash","Kiran","Rohith"]
L2 = [i.upper() for i in L1]
print(L1) #["Prakash","Kiran","Rohith"]
print(L2) #["PRAKASH","KIRAN","ROHITH"]

Ex: extract only even numbers from the list


-------------------------------------------
L1 = [1, 2, 3, 4, 5]
L2 = [i for i in L1 if i%2==0]
print(L1) #[1, 2, 3, 4, 5]
print(L2) #[2, 4]

common functions on list objects


--------------------------------
len(l)
max(l)
min(l)
sorted(l)
sorted(l,reverse=True)
sum(l)

Ex:
---
L = [1, 5, 2, 4, 3]

print(L) #[1,5,2,4,3]
print(len(L)) #5
print(max(L)) #5
print(min(L)) #1
print(sorted(L)) #[1,2,3,4,5]
print(sorted(L,reverse=True)) #[5,4,3,2,1]
print(sum(L)) #15

list specific methods:


----------------------
append(obj)
-----------
it is used to add an obj into list at the ending.

Ex:
---
L = [10, 20, 30]
print(L) #[10, 20, 30]
L.append(40)
L.append(50)
print(L) #[10, 20, 30, 40, 50]

insert(index,obj)
-----------------
it is used to add an object into the list at the given location

Ex:
---
L = [10, 20, 30]
print(L) #[10, 20, 30]
L.insert(0,99)
print(L) #[99,10, 20, 30]

remove(obj)
-----------
it is used to remove an obj from the list.

Ex:
---
L = [10, 20, 30, 40, 50]
print(L) #[10, 20, 30, 40, 50]
L.remove(30)
print(L) #[10, 20, 40, 50]

pop(index)
----------
it is used to remove an obj located at given index value

Ex:
---
L = [10, 20, 30, 40, 50]
print(L) #[10, 20, 30, 40, 50]
L.pop(1)
print(L) #[10, 30, 40, 50]

pop()
-----
it is used to remove an obj located at end of given list

Ex:
---
L = [10, 20, 30, 40, 50]
print(L) #[10, 20, 30, 40, 50]
L.pop()
print(L) #[10, 20, 30, 40]

clear()
--------
it is used to remove all the objects from the given list

Ex:
---
L = [10, 20, 30, 40, 50]
print(L) #[10, 20, 30, 40, 50]
L.clear()
print(L) #[]

index(obj)
----------
it returns location of the given object

Ex:
---
L = [10, 20, 30, 40, 50]
print(L) #[10, 20, 30, 40, 50]
print(L.index(10)) #0
print(L.index(30)) #2
print(L.index(50)) #4

count(obj)
----------
it is used to find the number of occurrences of given obj

Ex:
---
L = [10, 20, 30, 10, 20]
print(L) #[10, 20, 30, 40, 50]
print(L.count(10)) #2
print(L.count(20)) #2
print(L.count(30)) #1
print(L.count(40)) #0

reverse()
---------
it is used to reverse the list

Ex:
---
L = [10, 40, 30, 50, 20]
print(L) #[10, 40, 30, 50, 20]
L.reverse()
print(L) #[20, 50, 30, 40, 10]

sort()
------
it sorts the given list in asc order

Ex:
---
L = [10, 40, 30, 50, 20]
print(L) #[10, 40, 30, 50, 20]
L.sort()
print(L) #[10, 20, 30, 40, 50]

sort(reverse=True)
------------------
it sorts the given list in desc order

Ex:
---
L = [10, 40, 30, 50, 20]
print(L) #[10, 40, 30, 50, 20]
L.sort(reverse=True)
print(L) #[50, 40, 30, 20, 10]

What is the difference between sort() and sorted()?


---------------------------------------------------
sort() ----> modification on list
sorted() --> return list

L = [10, 40, 30, 50, 20]


print(L) #[10, 40, 30, 50, 20]
L.sort()
print(L) #[10, 20, 30, 40, 50]

Ex:
---
L = [10, 40, 30, 50, 20]
print(L) #[10, 40, 30, 50, 20]
print(sorted(L)) #[10, 20, 30, 40, 50]
print(L) #[10, 40, 30, 50, 20]

Ex:
---
L = [1, 2, [3, 4], 5]
print(L)
L.reverse()
print(L)

C:\test>py test.py
[1, 2, [3, 4], 5]
[5, [3, 4], 2, 1]

tuple data structure


~~~~~~~~~~~~~~~~~~~~
introduction to tuple data structure
-----------------------------------
==> it is a group of objects of differenct types i.e. hetrogeneous
==> it is represented by using ()
==> index concept is allowed
==> insertion order is preserved
==> duplicates are allowed
==> it not growable in nature.
==> it is immutable object i.e. modifications are not allowed

Ex:
---
T = (10, 23.556, True, 1+4j, "abc")

print(T)
print(type(T))

C:\test>py test.py
(10, 23.556, True, (1+4j), 'abc')
<class 'tuple'>

creation of tuple objects


------------------------
1) ()
2) (obj1, obj2, obj3, obj4, ....)
3) tuple()
4) input() with tuple()
5) eval()

access tuple objects


-------------------
1) directly
2) index concept
3) slice operator
4) while loop
5) for each loop

operators applicable on tuple objects


------------------------------------
+ concatenation
* repeatetion
in membership checking
not in membership checking
< comparing
> do
<= do
>= do
== do
!= do

nested tuple:
------------
a tuple obj within another tuple obj is called as nested tuple

tuple aliasing:
--------------
providing alternative name for the existing tuple obj is called as tuple
aliasing

common functions on tuple objects


--------------------------------
len(l)
max(l)
min(l)
sorted(l)
sorted(l,reverse=True)
sum(l)

tuple specific methods:


----------------------
index(obj) : it returns location of the given object
count(obj) : it is used to find the number of occurrences of given obj

tuple packing and unpacking:


----------------------------
constructing a tuple object from individual objects is called as tuple
packing.

Ex:
---
a = 111
b = 222
c = 333
t = a,b,c #tuple packing

print(a,type(a)) #111 <class 'int'>


print(b,type(b)) #222 <class 'int'>
print(c,type(c)) #333 <class 'int'>
print(t,type(t)) #(111,222,333) <class 'tuple'>

converting tuple object into individual objects is called as tuple


unpacking

Ex:
---
t = (666,777,888,999)
a,b,c,d = t #tuple unpacking

print(a,type(a)) #666 <class 'int'>


print(b,type(b)) #777 <class 'int'>
print(c,type(c)) #888 <class 'int'>
print(d,type(d)) #999 <class 'int'>
print(t,type(t)) #(666,777,888,999) <class 'tuple'>

set data structure


------------------
introduction:
~~~~~~~~~~~~~
==> it is a group of objects of different types
==> it is represented by using {}
==> it is not index based data structure
==> insertion order is not preserved
==> duplicates are not allowed
==> slicing is not allowed
==> it s not growable in nature (add/remove)
==> mutable object i.e. modifications are allowed

creation of set objects


~~~~~~~~~~~~~~~~~~~~~~~
1) set()
2) {obj1, obj2, obj3, ...}
3) input() with set()
4) eval()

Ex:
---
s = {111, 222, 333, 444, 555}
print(s)
print(type(s))

C:\test>py test.py
{555, 444, 333, 222, 111}
<class 'set'>
Ex:
---
s = {}
print(s)
print(type(s))

C:\test>py test.py
{}
<class 'dict'>

Ex:
---
s = set()
print(s)
print(type(s))

C:\test>py test.py
set()
<class 'set'>

accessing set objects


---------------------
1) directly
2) for each loop

Ex:
---
s = {111, 222, 333, 444, 555}
print(s)
for i in s:
print(i)

Note: set obj contains only immutable objects

Ex:
---
s = {10, [20, 30, 40], 50, 60}
print(s)

common functions on set:


------------------------
len(s)
max(s)
min(s)
sorted(s)
sorted(s,reverse=True)
sum(s)

Ex:
---
s = {10, 50, 20, 30}

print(s) #{10, 50, 20, 30}


print(len(s)) #4
print(max(s)) #50
print(min(s)) #10
print(sorted(s)) #[10, 20, 30, 50]
print(sorted(s,reverse=True)) #[50, 30, 20, 10]
print(sum(s)) #110

set specific methods:


---------------------
s.add(obj)
----------
it is used to add an obj into set

Ex:
---
s = {10, 20, 30}
print(s) #{10,20,30}
s.add(40)
s.add(50)
print(s) #{10,20,30,40,50}

s.remove(obj)
-------------
it is used to remove an obj from set

Ex:
---
s = {10, 20, 30}
print(s) #{10,20,30}
s.remove(20)
print(s) #{10,30}

Ex:
---
s = {10, 20, 30}
print(s) #{10,20,30}
s.remove(40)
print(s) #{10,20, 30}

C:\test>py test.py
{10, 20, 30}
Traceback (most recent call last):
File "C:\test\test.py", line 3, in <module>
s.remove(40)
KeyError: 40

discard(obj)
------------
it is same as remove(), but is obj doesn't exists it wn't raise error

Ex:
---
s = {10, 20, 30}
print(s) #{10,20,30}
s.discard(40)
s.discard(20)
print(s) #{10,30}

clear()
-------
it removes all objs from the set

Ex:
---
s = {10, 20, 30}
print(s) #{10,20,30}
s.clear()
print(s) #set()

special set related operations:


-------------------------------
s1 = {1,2,3,4,5}
s2 = {4,5,6,7,8}
print(s1) #{1,2,3,4,5}
print(s2) #{4,5,6,7,8}
print(s1.union(s2)) #{1,2,3,4,5,6,7,8}
print(s1.intersection(s2)) #{4,5}
print(s1.difference(s2)) #{1,2,3}
print(s2.difference(s1)) #{6,7,8}
print(s1.symmetric_difference(s2)) #{1,2,3,6,7,8}

dict data structure:


~~~~~~~~~~~~~~~~~~~~
introduction:
-------------
==> collection of individual objects is called as list, tuple or set.
==> collection of key and value pairs is called as dict.
==> it is represented by using {}
==> <class 'dict'>

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four"}
print(d)
print(type(d))

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four'}
<class 'dict'>

C:\test>

==> index concept is not allowed, but keys will act as index.
==> dupliate keys are not allowed but values can be duplicated.

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Two"}
print(d)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Two'}

C:\test>

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 3:"Five"}
print(d)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Five', 4: 'Four'}

C:\test>

==> it is growable in nature (add/remove/update)


==> mutable i.e. modifications are allowed

creation of dict objects:


-------------------------
1. {}
2. {k1:v1, k2:v2, k3:v3,....}
3. dict()

accessing dict objects


----------------------
1) directly we can access
2) for each loop

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
for i in d.items():
print(i)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
(1, 'One')
(2, 'Two')
(3, 'Three')
(4, 'Four')
(5, 'Five')

C:\test>

dict specific methods:


----------------------
d[key] = value
--------------
it is used to add a key and value pair into dict

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
d[6] = "Six"
d[7] = "Seven"
print(d)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five', 6: 'Six', 7:
'Seven'}

del d[key]
----------
it is used to delete an item from the dict

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
del d[3]
print(d)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
{1: 'One', 2: 'Two', 4: 'Four', 5: 'Five'}

C:\test>

pop(key)
--------
it delete corresponding key and value pairs from the dict

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
d.pop(4)
print(d)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
{1: 'One', 2: 'Two', 3: 'Three', 5: 'Five'}

clear()
-------
it removes all the key and value pairs from the dict
Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
d.clear()
print(d)

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
{}

C:\test>

d.get(key)
----------
it returns value associated with given key, if not return None

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
print(d.get(4)) #Four
print(d.get(6)) #None

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
Four
None

C:\test>

d.get(key,default)
------------------
it returns value associated with given key, if not return default val

Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
print(d.get(4,"Not There")) #Four
print(d.get(6,"Not There")) #Not There

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
Four
Not There

C:\test>

d.keys() --> returns list of keys existed in dict


d.values()-> returns list of values existed in dict
d.items()--> returns list of key and value pairs in dict
--------------------------------------------------------
Ex:
---
d = {1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five"}

print(d)
print(d.keys())
print(d.values())
print(d.items())

C:\test>py test.py
{1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
dict_keys([1, 2, 3, 4, 5])
dict_values(['One', 'Two', 'Three', 'Four', 'Five'])
dict_items([(1, 'One'), (2, 'Two'), (3, 'Three'), (4, 'Four'), (5,
'Five')])

dict comphrension:
------------------
Ex:
---
import math

d1 = {i:i for i in range(6)}


d2 = {i:i*i for i in range(6)}
d3 = {i:i*i*i for i in range(6)}
print(d1)
print(d2)
print(d3)

C:\test>py test.py
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125}

Programs on Strings:
--------------------
01. read and write string
-------------------------
s = input("Enter any string: ")
print(s)

C:\test>py test.py
Enter any string: welcome
welcome

C:\test>

02. read string and print characters present at +ve and -ve indexes
-------------------------------------------------------------------
s = input("Enter any string: ")
print(s)

index1 = 0
index2 = -1

while index1<len(s) and index2>-(len(s)+1):


print(f"index1:{index1}, char:{s[index1]}, index2:{index2},
char:{s[index2]}")
index1 = index1 + 1
index2 = index2 - 1

C:\test>py test.py
Enter any string: abcd
abcd
index1:0, char:a, index2:-1, char:d
index1:1, char:b, index2:-2, char:c
index1:2, char:c, index2:-3, char:b
index1:3, char:d, index2:-4, char:a

03. print characters present at even index values


-------------------------------------------------
s = input("Enter any string: ")
print(s)

index = 0

while index<len(s):
if index%2==0:
print(f"index:{index}, char:{s[index]}")
index=index+1

C:\test>py test.py
Enter any string: welcome
welcome
index:0, char:w
index:2, char:l
index:4, char:o
index:6, char:e

C:\test>

04. print characters present at odd index values


-------------------------------------------------
s = input("Enter any string: ")
print(s)

index = 0

while index<len(s):
if index%2!=0:
print(f"index:{index}, char:{s[index]}")
index=index+1

C:\test>py test.py
Enter any string: welcome
welcome
index:1, char:e
index:3, char:c
index:5, char:m

05. count number of vowels present in the given string


------------------------------------------------------
import re

def fun(s):
return len(re.findall("[aeiou]",s))

s = input("Enter any string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter any string: welcome
welcome
['e', 'o', 'e']

C:\test>py test.py
Enter any string: welcome
welcome
3

C:\test>py test.py
Enter any string: programming
programming
3

06. count number of consonants present in the given string


----------------------------------------------------------
import re

def fun(s):
return len(re.findall("[^aeiou]",s))

s = input("Enter any string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter any string: welcome
welcome
4

C:\test>py test.py
Enter any string: programming
programming
8

07. count number of alphabets


-----------------------------
import re

def fun(s):
return len(re.findall("[a-zA-Z]",s))

s = input("Enter any string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter any string: welcome
welcome
7

C:\test>py test.py
Enter any string: wel#come@
wel#come@
7

C:\test>py test.py
Enter any string: WeL123COme
WeL123COm

08. count number of digits present in the string


------------------------------------------------
import re

def fun(s):
return len(re.findall("[0-9]",s))

s = input("Enter any string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter any string: acb123def
acb123def
3

C:\test>py test.py
Enter any string: abc$123^6u
abc$123^6u
4

C:\test>

09. count number of spaces


--------------------------
import re

def fun(s):
return len(re.findall("[ ]",s))
s = input("Enter any string: ")
print(s)
print(fun(s))

C:\test>py test.py
Enter any string: abc def xyz 123
abc def xyz 123
3

C:\test>

10. count number of special characters


--------------------------------------
import re

def fun(s):
return len(re.findall("[^a-zA-Z0-9 ]",s))

s = input("Enter any string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter any string: abc 345 d#e%f 7*9&8^0
abc 345 d#e%f 7*9&8^0
5

11. reverse of the given string


-------------------------------
def fun1(s):
return s[::-1]

def fun2(s):
res = ""
for i in s:
res=i+res
return res

s=input("Enter any string: ")


print(fun1(s))
print(fun2(s))

C:\test>py test.py
Enter any string: abcd
dcba
dcba

C:\test>py test.py
Enter any string: prakash
hsakarp
hsakarp

12. convert given string into upper case


----------------------------------------
def fun1(s):
return s.upper()

def fun2(s):
res = ""
for i in s:
res=res+chr(ord(i)-32)
return res

s=input("Enter any string: ")


print(fun1(s))
print(fun2(s))

C:\test>py test.py
Enter any string: abc
ABC
ABC

C:\test>py test.py
Enter any string: prakash
PRAKASH
PRAKASH

13. convert the given string into lower case


--------------------------------------------
def fun1(s):
return s.lower()

def fun2(s):
res = ""
for i in s:
if i.isspace():
res=res+i
else:
res=res+chr(ord(i)+32)
return res

s=input("Enter any string: ")


print(fun1(s))
print(fun2(s))

C:\test>py test.py
Enter any string: ABCDE
abcde
abcde

C:\test>py test.py
Enter any string: PYTHON AND JAVA
python and java
python and java

14. toggle case or swap case


----------------------------
def fun1(s):
return s.swapcase()

def fun2(s):
res = ""
for i in s:
if i.islower():
res = res + chr(ord(i)-32)
else:
res = res + chr(ord(i)+32)
return res

s=input("Enter any string: ")


print(fun1(s))
print(fun2(s))

C:\test>py test.py
Enter any string: PyThOn
pYtHoN
pYtHoN

15. convert even indexed char into upper case and odd indexed char into
lower case
-----------------------------------------------------------------------
def fun(s):
res = ""
for i in range(len(s)):
if i%2==0:
res = res + s[i].upper()
else:
res = res + s[i].lower()
return res

s=input("Enter any string: ")


print(fun(s))

C:\test>py test.py
Enter any string: python
PyThOn

16. convert odd indexed char into upper case and eve indexed char into
lower case
-----------------------------------------------------------------------
def fun(s):
res = ""
for i in range(len(s)):
if i%2!=0:
res = res + s[i].upper()
else:
res = res + s[i].lower()
return res

s=input("Enter any string: ")


print(fun(s))
C:\test>py test.py
Enter any string: python
pYtHoN

17. reverse the entire sentence


-------------------------------
def fun(s):
return s[::-1]

s=input()
print(s)
print(fun(s))

C:\test>py test.py
python is very easy to understand
python is very easy to understand
dnatsrednu ot ysae yrev si nohtyp

18. reverse even indexed words


-------------------------------
def fun(s):
L = []
index = 0
for i in s.split(" "):
if index%2==0:
L.append(i[::-1])
else:
L.append(i)
index=index+1
return ' '.join(L)

s=input()
print(s)
print(fun(s))

C:\test>py test.py
python is very easy to understand
python is very easy to understand
nohtyp is yrev easy ot understand

19. reverse odd indexed words


-------------------------------
def fun(s):
L = []
index = 0
for i in s.split(" "):
if index%2!=0:
L.append(i[::-1])
else:
L.append(i)
index=index+1
return ' '.join(L)
s=input()
print(s)
print(fun(s))

C:\test>py test.py
python is very easy to understand
python is very easy to understand
python si very ysae to dnatsrednu

20. convert each word first char into upper case


------------------------------------------------
def fun(s):
L = []
for i in s.split(" "):
L.append(i[0].upper()+i[1:])
return ' '.join(L)

s=input()
print(s)
print(fun(s))

C:\test>py test.py
python is very easy to understand
python is very easy to understand
Python Is Very Easy To Understand

21. convert each word first & last char into upper case
--------------------------------------------------------
def fun(s):
L = []
for i in s.split(" "):
L.append(i[0].upper()+i[1:len(i)-1]+i[-1].upper())
return ' '.join(L)

s=input()
print(s)
print(fun(s))

C:\test>py test.py
python is very easy to understand
python is very easy to understand
PythoN IS VerY EasY TO UnderstanD

22.convert except first and last char into upper case


-----------------------------------------------------
def fun(s):
L = []
for i in s.split(" "):
L.append(i[0]+i[1:len(i)-1].upper()+i[-1])
return ' '.join(L)
s=input()
print(s)
print(fun(s))

C:\test>py test.py
python is very easy to understand
python is very easy to understand
pYTHOn is vERy eASy to uNDERSTANd

23. convert even length words into upper case


---------------------------------------------
def fun(s):
L = []
for i in s.split(" "):
if len(i)%2==0:
L.append(i.upper())
else:
L.append(i)
return ' '.join(L)

s=input()
print(s)
print(fun(s))

C:\test>py test.py
python was very easy to implement and understand
python was very easy to implement and understand
PYTHON was VERY EASY TO implement and UNDERSTAND

24) return middle char(s) from the given string


-----------------------------------------------
def fun(s):
if len(s)%2!=0:
return s[len(s)//2]
else:
return s[len(s)//2-1:len(s)//2+1]

s=input()
print(s)
print(fun(s))

C:\test>py test.py
abc
abc
b

C:\test>py test.py
abcd
abcd
bc
C:\test>py test.py
abcde
abcde
c

C:\test>py test.py
abcdef
abcdef
cd

25. sort the string (sort the characters present in the given str)
------------------------------------------------------------------
def fun(s):
l = list(s)
l.sort()
return ''.join(l)

s=input()
print(s)
print(fun(s))

C:\test>py test.py
test
test
estt

C:\test>py test.py
demo
demo
demo

C:\test>py test.py
prakash
prakash
aahkprs

26. sort the names


-------------------
l = ["pqr","mno","abc","xyz","ijk"]
print(l)
l.sort()
print(l)

C:\test>py test.py
['pqr', 'mno', 'abc', 'xyz', 'ijk']
['abc', 'ijk', 'mno', 'pqr', 'xyz']

27. remove duplicate characters


-------------------------------
def fun(s):
output=[]
for i in s:
if i not in output:
output.append(i)
return ''.join(output)

s = input("Enter string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter string: welcome
welcome
welcom

C:\test>py test.py
Enter string: abcdabcaba
abcdabcaba
abcd

28. remove vowels from the given string


---------------------------------------
import re
def fun(s):
return re.sub("[aeiou]","",s)

s = input("Enter string: ")


print(s)
print(fun(s))

C:\test>py test.py
Enter string: welcome
welcome
wlcm

C:\test>py test.py
Enter string: prakash
prakash
prksh

29. check whether the given string is paliandrome or not


--------------------------------------------------------
def fun(s):
low = 0
high = len(s)-1
while low<=high:
if s[low]!=s[high]:
return False
low = low + 1
high = high - 1
return True

s = input("Enter string: ")


print(s)
print(fun(s))
C:\test>py test.py
Enter string: prakash
prakash
False

C:\test>py test.py
Enter string: madam
madam
True

C:\test>py test.py
Enter string: liril
liril
True

30. check whether the string is rotation of another string or not?


-----------------------------------------------------------------
abcd ---> abcd, bcda, cdab, dabc

abcdabcd

def fun(s1,s2):
if len(s1)!=len(s2):
return False
temp = s1+s1
return temp.find(s2)!=-1

s1 = input("Enter string: ")


s2 = input("Enter string: ")
print(fun(s1,s2))

C:\test>py test.py
Enter string: abcd
Enter string: bcda
True

C:\test>py test.py
Enter string: abcd
Enter string: cdab
True

C:\test>py test.py
Enter string: abcde
Enter string: bcade
False

Programs on List data structures:


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

1. read and write elements in the list


--------------------------------------
l = eval(input("Enter list of values: "))
print(l)
for i in l:
print(i)

C:\test>py test.py
Enter list of values: [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
10
20
30
40
50

2. sum of elements present in the list


--------------------------------------
def fun(l):
s = 0
for i in l:
s = s + i
return s

l = [1, 2, 3, 4, 5]
print(fun(l)) #1+2+3+4+5=15

C:\test>py test.py
15

C:\test>

3. average of elements present in the list


-------------------------------------------
def fun(l):
if len(l)==0:
return 0.0
s = 0
for i in l:
s = s + i
return s/len(l)

print(fun([1, 2, 3, 4, 5]))
print(fun([]))

C:\test>py test.py
3.0
0.0

4. generate a list with even and odd elements in the range of 0 to 10


---------------------------------------------------------------------
def fun():
l1 = [i for i in range(0,11) if i%2==0]
l2 = [i for i in range(0,11) if i%2!=0]
return l1,l2

even,odd = fun()
print(even)
print(odd)

C:\test>py test.py
[0, 2, 4, 6, 8, 10]
[1, 3, 5, 7, 9]

C:\test>

5. get smaller elements from a list less then the given element x
-----------------------------------------------------------------
def fun(l,x):
return [i for i in l if i<x]

l = [9, 11, 15, 12, 3, 7, 14, 10]


x = 10
print(l)
print(fun(l,x)) #[9, 3, 7]

C:\test>py test.py
[9, 11, 15, 12, 3, 7, 14, 10]
[9, 3, 7]

C:\test>

6. generate 10 random numbers from 1 to 20 and append to the list


-----------------------------------------------------------------
import random
def fun():
return [random.randint(1,20) for i in range(10)]

print(fun())

C:\test>py test.py
[17, 2, 9, 12, 1, 4, 9, 10, 6, 9]

C:\test>py test.py
[14, 17, 3, 2, 13, 4, 10, 18, 3, 18]

C:\test>py test.py
[1, 9, 4, 15, 11, 6, 10, 19, 7, 14]

7. swap first and last element in the list


------------------------------------------
def fun(L):
L[0],L[-1] = L[-1],L[0]

L = [1, 2, 3, 4, 5]
print(L) #[1, 2, 3, 4, 5]
fun(L)
print(L) #[5, 2, 3, 4, 1]

C:\test>py test.py
[1, 2, 3, 4, 5]
[5, 2, 3, 4, 1]

C:\test>

8. count number of elements greater than x


------------------------------------------
def fun(L,x):
return len([i for i in L if i>x])

L = [9, 11, 15, 12, 3, 7, 14, 10]


x = 10
print(fun(L,x)) #4

9. reverse of the given list


----------------------------
version1:
---------
def fun(L):
return L.reverse()

L = [9, 11, 15, 12, 3, 7, 14, 10]


print(L) # [9, 11, 15, 12, 3, 7, 14, 10]
fun(L)
print(L) # [10, 14, 7, 3, 12, 15, 11, 9]

version2:
---------
def fun(L,start,end):
while start<=end:
L[start],L[end]=L[end],L[start]
start=start+1
end=end-1

L = [9, 11, 15, 12, 3, 7, 14, 10]


print(L) # [9, 11, 15, 12, 3, 7, 14, 10]
fun(L,0,len(L)-1)
print(L) # [10, 14, 7, 3, 12, 15, 11, 9]

10. get the index of the given element


--------------------------------------
def fun(L,x):
for i in range(len(L)):
if x==L[i]:
return i
return None

print(fun([1,2,3,4,5],1)) #0
print(fun([1,2,3,4,5],5)) #4
print(fun([1,2,3,4,5],6)) #None
11. find the largest / max element in the list
----------------------------------------------
def fun(L):
if not L:
return None
m = L[0]
for i in range(1,len(L)):
if L[i]>m:
m = L[i]
return m

print(fun([1,3,2,5,4])) #5
print(fun([1,3,2,-5,4])) #4
print(fun([1])) #1
print(fun([])) #None

12. find the smallest / min element in the list


-----------------------------------------------
def fun(L):
if not L:
return None
m = L[0]
for i in range(1,len(L)):
if L[i]<m:
m = L[i]
return m

print(fun([1,3,2,5,4])) #1
print(fun([1,3,2,-5,4])) #-5
print(fun([1])) #1
print(fun([])) #None

13. second largest / max element in the list


--------------------------------------------
def fun1(l):
if not l:
return None
return max(l)

def fun2(l):
if len(l)<=1:
return None
lar = fun1(l)
slar = None
for i in l:
if i!=lar:
if slar==None:
slar = i
else:
slar = max(slar,i)
return slar

print(fun2([1,3,2,5,4])) #4
print(fun2([1,3,2,-5,4])) #3
print(fun2([1])) #None
print(fun2([])) #None

14. check whether a list is sorted or not


-----------------------------------------
def fun(l):
i=1
while i<len(l):
if l[i]<l[i-1]:
return False
i=i+1
return True

print(fun([1,2,3,4,5])) #True
print(fun([1,2,5,3,4])) #False

15. merge two list objects into third list


------------------------------------------
def fun(l1,l2):
l=[]
for i in l1:
l.append(i)
for i in l2:
l.append(i)
return l

l1 = [1, 3, 5, 2, 4]
l2 = [6, 8, 7, 9, 10]
l3 = fun(l1,l2)
print(l1) #[1, 3, 5, 2, 4]
print(l2) #[6, 8, 7, 9, 10]
print(l3) #[1, 3, 5, 2, 4, 6, 8, 7, 9, 10]

16. merge two list objects into third list and sort
---------------------------------------------------
def fun(l1,l2):
l=l1+l2
l.sort()
return l

l1 = [1, 3, 5, 2, 4]
l2 = [6, 8, 7, 9, 10]
l3 = fun(l1,l2)
print(l1) #[1, 3, 5, 2, 4]
print(l2) #[6, 8, 7, 9, 10]
print(l3) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

17. union of two list objects


-----------------------------
def fun(l1,l2):
l3=[]
for i in l1:
if i not in l3:
l3.append(i)
for i in l2:
if i not in l3:
l3.append(i)
return l3

l1 = [1, 2, 3, 4, 5]
l2 = [4, 5, 6, 7, 8]
print(fun(l1,l2)) #[1, 2, 3, 4, 5, 6, 7, 8]

18. intersection of two list objects


------------------------------------
def fun(l1,l2):
l3=[]
for i in l1:
if i in l2:
l3.append(i)
return l3

l1 = [1, 2, 3, 4, 5]
l2 = [4, 5, 6, 7, 8]
print(fun(l1,l2)) #[4,5]

19. find cumulative sum of elements in the list


------------------------------------------------
def fun(l):
l1 = []
s = 0
for i in l:
s=s+i
l1.append(s)
return l1

l = [1, 2, 3, 4, 5]
print(fun(l)) #[1, 3, 6, 10, 15]

20. check all the elements in list are unique or not


----------------------------------------------------
def fun(l):
return len(l)==len(set(l))

print(fun([1, 2, 3])) #True


print(fun([1, 1, 2, 3])) #False
print(fun([1, 1, 1])) #False
print(fun([1, 1])) #False
print(fun([1])) #True

21. left rotate a list by one unit


----------------------------------
version1:
---------
L = [1, 2, 3, 4, 5]
print(L) #[1, 2, 3, 4, 5]
#by slice operation
L = L[1:] + L[0:1]
print(L) #[2, 3, 4, 5, 1]

version2:
---------
L = [1, 2, 3, 4, 5, 6]
print(L) #[1, 2, 3, 4, 5, 6]
#by using insertion and deletion
L.append(L.pop(0))
print(L) #[2, 3, 4, 5, 6, 1]

version3:
---------
def fun(L):
n = len(L)
x = L[0]
for i in range(1,n):
L[i-1] = L[i]
L[n-1] = x

L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
#by manual impl
fun(L)
print(L) #[2, 3, 4, 5, 6, 7, 1]

22. right rotate a list by one unit


----------------------------------
version1:
---------
L = [1, 2, 3, 4, 5]
print(L) #[1, 2, 3, 4, 5]
#by slice operation
L = L[-1:-2:-1] + L[:len(L)-1]
print(L) #[5, 1, 2, 3, 4]

version2:
---------
L = [1, 2, 3, 4, 5, 6]
print(L) #[1, 2, 3, 4, 5, 6]
#by using insertion and deletion
L.insert(0,L.pop())
print(L) #[6, 1, 2, 3, 4, 5]

version3:
---------
def fun(L):
n = len(L)
x = L[n-1]
for i in range(n-1,0,-1):
L[i] = L[i-1]
L[0] = x

L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
#by manual impl
fun(L)
print(L) #[7, 1, 2, 3, 4, 5, 6]

23. left rotate a list by d positions


-------------------------------------
version1:
---------
L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
d = 2
L = L[d:] + L[:d]
print(L) #[3, 4, 5, 6, 7, 1, 2]

version2:
---------
L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
d = 3
for i in range(0,d):
L.append(L.pop(0))
print(L) #[4, 5, 6, 7, 1, 2, 3]

version3:
---------
from collections import deque
L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
d = 2
dq = deque(L)
dq.rotate(-d) #-d left rotate
L = list(dq)
print(L) #[3, 4, 5, 6, 7, 1, 2]

version4:
---------
def reverse(L,b,e):
while b<e:
L[b],L[e] = L[e],L[b]
b=b+1
e=e-1

def fun(L,d):
n=len(L)
reverse(L,0,d-1)
reverse(L,d,n-1)
reverse(L,0,n-1)

L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
d = 2
print(L) #[1, 2, 3, 4, 5, 6, 7, 8, 9]
fun(L,d)
print(L) #[3, 4, 5, 6, 7, 8, 9, 1, 2]

24. right rotate a list by d positions


-------------------------------------
version1:
---------
L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
d = 2
L = L[len(L)-d:] + L[:len(L)-d]
print(L) #[6, 7, 1, 2, 3, 4, 5]

version2:
---------
L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
d = 3
for i in range(0,d):
L.insert(0,L.pop(-1))
print(L) #[5, 6, 7, 1, 2, 3, 4]

version3:
---------
from collections import deque
L = [1, 2, 3, 4, 5, 6, 7]
print(L) #[1, 2, 3, 4, 5, 6, 7]
d = 2
dq = deque(L)
dq.rotate(d) #d right rotate
L = list(dq)
print(L) #[6, 7, 1, 2, 3, 4, 5]

version4:
---------
def reverse(L,b,e):
while b<e:
L[b],L[e] = L[e],L[b]
b=b+1
e=e-1

def fun(L,d):
n=len(L)
reverse(L,0,n-1)
reverse(L,0,d-1)
reverse(L,d,n-1)

L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
d = 2
print(L) #[1, 2, 3, 4, 5, 6, 7, 8, 9]
fun(L,d)
print(L) #[8, 9, 1, 2, 3, 4, 5, 6, 7]

programs on matrices
~~~~~~~~~~~~~~~~~~~~
collection of rows and cols ----> matrix

1 2 3
4 5 6
7 8 9

order of matrix ----> num of rows x num of cols


3 x 3

n x m ----> n num of rows and m num of cols

1. read and write matrix elements


---------------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)
print()
printm(L,m,n)

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9

1 2 3
4 5 6
7 8 9

2. sum of each element present in an array


------------------------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)
def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n):
s=0
for i in range(m):
for j in range(n):
s=s+L[i][j]
return s

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)
print()
printm(L,m,n)
print()
print("Sum of elements in matr:",fun(L,m,n))

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9

1 2 3
4 5 6
7 8 9

Sum of elements in matr: 45

3. addition of two matrices


---------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def init(L,m,n):
for i in range(m):
TL = [0 for i in range(n)]
L.append(TL)
def addition(L1,m,n,L2,L3):
for i in range(m):
for j in range(n):
L3[i][j] = L1[i][j] + L2[i][j]

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L1 = []
read(L1,m,n)
L2 = []
read(L2,m,n)
L3 = []
init(L3,m,n)
addition(L1,m,n,L2,L3)

print()
printm(L1,m,n)
print()
printm(L2,m,n)
print()
printm(L3,m,n)

C:\test>py test.py
Enter num of rows: 2
Enter num of cols: 2
1 2
3 4
5 6
7 8

1 2
3 4

5 6
7 8

6 8
10 12

4. subtraction of two matrices


------------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def init(L,m,n):
for i in range(m):
TL = [0 for i in range(n)]
L.append(TL)

def subtraction(L1,m,n,L2,L3):
for i in range(m):
for j in range(n):
L3[i][j] = L1[i][j] - L2[i][j]

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L1 = []
read(L1,m,n)
L2 = []
read(L2,m,n)
L3 = []
init(L3,m,n)
subtraction(L1,m,n,L2,L3)

print()
printm(L1,m,n)
print()
printm(L2,m,n)
print()
printm(L3,m,n)

C:\test>py test.py
Enter num of rows: 2
Enter num of cols: 4
1 2 3 4
5 6 7 8
1 1 1 1
2 2 2 2

1 2 3 4
5 6 7 8

1 1 1 1
2 2 2 2

0 1 2 3
3 4 5 6

5. multiplication of two matrices


---------------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()
def init(L,m,n):
for i in range(m):
TL = [0 for i in range(n)]
L.append(TL)

def multiplication(L1,m,n,L2,L3):
for i in range(m):
for j in range(n):
for k in range(n):
L3[i][j] = L3[i][j] + L1[i][k]*L2[k][j]

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L1 = []
read(L1,m,n)
L2 = []
read(L2,m,n)
L3 = []
init(L3,m,n)
multiplication(L1,m,n,L2,L3)

print("Matrix: A")
printm(L1,m,n)
print("Matrix: B")
printm(L2,m,n)
print("Matrix: C")
printm(L3,m,n)

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9
1 0 0
0 1 0
0 0 1
Matrix: A
1 2 3
4 5 6
7 8 9
Matrix: B
1 0 0
0 1 0
0 0 1
Matrix: C
1 2 3
4 5 6
7 8 9

C:\test>py test.py
Enter num of rows: 2
Enter num of cols: 2
1 2
3 4
5 6
7 8
Matrix: A
1 2
3 4
Matrix: B
5 6
7 8
Matrix: C
19 22
43 50

6. sum wise sum calculation


---------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n):
for i in range(m):
s=0
for j in range(n):
s=s+L[i][j]
print(f"row: {i} and sum: {s}")

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)
fun(L,m,n)

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9
Matrix: A
1 2 3
4 5 6
7 8 9
row: 0 and sum: 6
row: 1 and sum: 15
row: 2 and sum: 24

7. col wise sum calculation


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

def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n):
for i in range(m):
s=0
for j in range(n):
s=s+L[j][i]
print(f"col: {i} and sum: {s}")

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)
fun(L,m,n)

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9
Matrix: A
1 2 3
4 5 6
7 8 9
col: 0 and sum: 12
col: 1 and sum: 15
col: 2 and sum: 18

8. sum of diagonal elements


---------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)
def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n):
s=0
for i in range(m):
s=s+L[i][i]
return s

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)
print(fun(L,m,n))

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9
Matrix: A
1 2 3
4 5 6
7 8 9
15

9. sum of opposite diagonal elements


-------------------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n):
s=0
for i in range(m):
s=s+L[i][m-i-1]
return s

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)
print(fun(L,m,n))

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 0 0
0 1 0
0 0 1
Matrix: A
1 0 0
0 1 0
0 0 1
1

10. transpose of the given matrix


---------------------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def printmm(L,m,n):
for i in range(m):
for j in range(n):
print(L[j][i],end=' ')
print()

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)
print("Transpose Matrix: A")
printmm(L,m,n)

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9
Matrix: A
1 2 3
4 5 6
7 8 9
Transpose Matrix: A
1 4 7
2 5 8
3 6 9

11. identity matrix or not


--------------------------
1 2 3
4 5 6
7 8 9 Not

1 0 0
0 1 0
0 0 1 Yes

diagonal elements ---> 1


non-dia elements ----> 0

def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def identity(L,m,n):
for i in range(m):
for j in range(n):
if i==j and L[i][j]!=1:
return False
if i!=j and L[i][j]!=0:
return False
return True

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)
print(identity(L,m,n))

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 2 3
4 5 6
7 8 9
Matrix: A
1 2 3
4 5 6
7 8 9
False

C:\test>py test.py
Enter num of rows: 3
Enter num of cols: 3
1 0 0
0 1 0
0 0 1
Matrix: A
1 0 0
0 1 0
0 0 1
True

12. swaping of two rows


-----------------------
1 2 3
4 5 6
7 8 9

row 1 and 3

7 8 9
4 5 6
1 2 3

def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n,x,y):
L[x-1],L[y-1] = L[y-1],L[x-1]

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)

x = int(input("Enter row1 value to swap: "))


y = int(input("Enter row2 value to swap: "))
fun(L,m,n,x,y)

print("Updated Matrix: A")


printm(L,m,n)

C:\test>py test.py
Enter num of rows: 4
Enter num of cols: 4
1 2 3 4
5 5 5 5
6 7 8 9
2 2 2 2
Matrix: A
1 2 3 4
5 5 5 5
6 7 8 9
2 2 2 2
Enter row1 value to swap: 1
Enter row2 value to swap: 3
Updated Matrix: A
6 7 8 9
5 5 5 5
1 2 3 4
2 2 2 2

13. swaping of two cols


-----------------------
1 2 3
4 5 6
7 8 9

1 and 2 col

2 1 3
5 4 6
8 7 9

def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n,x,y):
for i in range(m):
L[i][x-1],L[i][y-1] = L[i][y-1],L[i][x-1]

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)

x = int(input("Enter col1 value to swap: "))


y = int(input("Enter col2 value to swap: "))
fun(L,m,n,x,y)

print("Updated Matrix: A")


printm(L,m,n)

C:\test>py test.py
Enter num of rows: 4
Enter num of cols: 4
1 2 3 4
5 5 5 5
6 7 8 9
2 2 2 2
Matrix: A
1 2 3 4
5 5 5 5
6 7 8 9
2 2 2 2
Enter col1 value to swap: 1
Enter col2 value to swap: 4
Updated Matrix: A
4 2 3 1
5 5 5 5
9 7 8 6
2 2 2 2

14. swaping of diagonal


-----------------------
def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def fun(L,m,n):
for i in range(m):
L[i][i],L[i][m-i-1] = L[i][m-i-1],L[i][i]
m = int(input("Enter num of rows: "))
n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix: A")
printm(L,m,n)

fun(L,m,n)

print("Updated Matrix: A")


printm(L,m,n)

C:\test>py test.py
Enter num of rows: 4
Enter num of cols: 4
1 1 2 2
3 3 4 4
5 5 6 6
7 7 8 8
Matrix: A
1 1 2 2
3 3 4 4
5 5 6 6
7 7 8 8
Updated Matrix: A
2 1 2 1
3 4 3 4
5 6 5 6
8 7 8 7

15. rotating matrix by 90 degrees


---------------------------------
1. transpose of given mat
2. reverse the matrix w.r.t row last to first row

def read(L,m,n):
for i in range(m):
TL = [int(i) for i in input().split()]
L.append(TL)

def printm(L,m,n):
for i in range(m):
for j in range(n):
print(L[i][j],end=' ')
print()

def printmm(L,m,n):
for i in range(m-1,-1,-1): #2,1,0
for j in range(n):
print(L[i][j],end=' ')
print()
def init(L,m,n):
for i in range(m):
TL = [0 for i in range(n)]
L.append(TL)

def fun(L,m,n):
#1st transpose
LL = []
init(LL,m,n)
for i in range(m):
for j in range(n):
LL[i][j] = L[j][i]
#2nd print in reverse w.r.t row
printmm(LL,m,n)

m = int(input("Enter num of rows: "))


n = int(input("Enter num of cols: "))
L = []
read(L,m,n)

print("Matrix")
printm(L,m,n)
print("After 90 degrees rotation updated Matrix")
fun(L,m,n)

Recursion and Recursion Based Application


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Introduction:
~~~~~~~~~~~~~
a function / method which is called by itself is called as recursion &
the method / function which is invoked in this process is called as
recursive method.

Ex:
---
def fun():
print("Good morning")

fun()

C:\test>py test.py
Good morning

Ex:
---
def fun():
print("Good morning")
fun()

fun()

Good morning
Good morning
Good morning
Good morning
RecursionError: maximum recursion depth exceeded while calling a Python
object

There are two types of recursion are existed

1. Finate Recursion
2. Infinate Recursion

InFinate Recursion
------------------
There is no proper condition to stop the recursion, it will keep on
executing, hence our internal stack is full it overflows, then python
raises error message saying Recursion Error.

Ex:
---
def fun():
print("Good morning")
fun()

fun()

Good morning
Good morning
Good morning
Good morning
RecursionError: maximum recursion depth exceeded while calling a Python
object

Finate Recursion:
-----------------
This recursion will stop at cetertain point by giving some condition.
this condition is called as base condition. then recursive call will end
at some point.

Ex:
---
def fun(n):
if n==0: #base condition
return
print("Good morning")
fun(n-1)

fun(5)

C:\test>py test.py
Good morning
Good morning
Good morning
Good morning
Good morning

C:\test>
base condition:
---------------
inside recursion, to stop recursion process at a finate steps we have to
construct a condition, such that out function call should terminate at
finate steps this condition inside the function is called as base
condition. based on our requirement we can change this condition..

based on fuction calls we have two types recursions are there

1) direct recursion:
--------------------
def fun():
-----------
-----------
-----------
fun()

2) indirection recursion:
-------------------------
def fun1():
-----------
-----------
-----------
fun2()

def fun2():
-----------
-----------
-----------
fun1()

When we can go for recursion?


-----------------------------
When a problem size is very big and it is made up of small small sub
problems then recommended to for recursion.

Applications of recursion
-------------------------
binary search algorithm
quick sort
merge sort
divide and conquer apps
factorial
prime
linked list
tree
graphs etc

based on position of recursive statement again it is divided into 2 type


1. tail recursion --------> perfect recursion
2. non-tail recursion ----> back tracking

tail recursion:
---------------
a recursive function is called tail recursion if the function does not
contain any statements after recursion call.

Ex:
---
def fun(n):
if n==0:
return
print(n)
fun(n-1) #recursion call

fun(5)

C:\test>py test.py
5
4
3
2
1

C:\test>

non tail recursion:


---------------
a recursive function is called non- tail recursion if the function
contain any statements after recursion call.

Ex:
---
def fun(n):
if n==0:
return
fun(n-1) #recursion call
print(n)

fun(5)

C:\test>py test.py
1
2
3
4
5

C:\test>

01. sum of n natural numbers


----------------------------
def fun(n):
if n==0:
return 0
return n+fun(n-1)

print(fun(4)) #0+1+2+3+4=10
print(fun(5)) #0+1+2+3+4+5=15

C:\test>py test.py
10
15

C:\test>

02. factorial of the given number


---------------------------------
def fun(n):
if n==0:
return 1
return n*fun(n-1)

print(fun(4)) #1*2*3*4=24
print(fun(5)) #1*2*3*4*5=120

C:\test>py test.py
24
120

C:\test>

03. sum of digits present in the given num


-------------------------------------------
def fun(n):
if n==0:
return 0
return n%10+fun(n//10)

print(fun(1283)) #3+8+2+1=14

C:\test>py test.py
24
120

C:\test>

04. fibonacci sequence


----------------------
def fib(n):
if n==0 or n==1:
return n
return fib(n-1) + fib(n-2)

#0 1 1 2 3 5 8
for i in range(10):
print(fib(i),end=' ')
C:\test>py test.py
0 1 1 2 3 5 8 13 21 34

05. a to the power b


--------------------
2*3 ----> 2*2*2 = 8
2*8 ----> 2*2*2*2*2*2*2*2 = 256

def fun(a,b):
if b==0:
return 1
return a*fun(a,b-1)

print(fun(2,0)) #1
print(fun(2,1)) #2
print(fun(2,2)) #4
print(fun(2,3)) #8
print(fun(2,8)) #256

C:\test>py test.py
1
2
4
8
256

6. product of two integer values by using recursion


---------------------------------------------------
def fun(a,b):
if a<b:
return fun(b,a)
elif b!=0:
return a+fun(a,b-1)
else:
return 0

print(fun(4,2)) #8
print(fun(3,2)) #6
print(fun(1,6)) #6
print(fun(8,0)) #0

7. prime number or not


----------------------
def prime(n,i):
if i==1:
return True
elif n%i==0:
return False
return prime(n,i-1)

print(prime(4,4//2)) #False
print(prime(5,5//2)) #True
print(prime(100,100//2)) #False

C:\test>py test.py
False
True
False

8. reverse the given number


---------------------------
def rev(n,len):
if n==0:
return 0
return (n%10)*(10**len-1) + rev(n//10,len-1)

print(rev(123,3)) #321
print(rev(782307,6)) #703287

C:\test>py test.py
3204
7032843

9. decimal to binary conversion


-------------------------------
def fun(n):
if n==0:
return 0
return (n%2)+10*fun(n//2)

for i in range(21):
print(f"{i}\t{fun(i)}")

C:\test>py test.py
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
16 10000
17 10001
18 10010
19 10011
20 10100
10. dec to oct
--------------
def fun(n):
if n==0:
return 0
return (n%8)+10*fun(n//8)

for i in range(21):
print(f"{i}\t{fun(i)}")

C:\test>py test.py
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 10
9 11
10 12
11 13
12 14
13 15
14 16
15 17
16 20
17 21
18 22
19 23
20 24

11. dec to hexa


---------------
def fun(n):
if n==0:
return 0
return (n%16)+10*fun(n//16)

for i in range(21):
print(f"{i}\t{fun(i)}")

C:\test>py test.py
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 14
15 15
16 10
17 11
18 12
19 13
20 14

12. reverse of the given string


-------------------------------
def fun(s):
if s=="" or len(s)<=1:
return s+""
return fun(s[1:])+s[0]

print(fun("abcd")) #dcba
print(fun("abba")) #abba

13. towers of hanoi


-------------------
Rules:
------
1. only one disk can be moved at a time
2. each move consits of taking upper disck from top of tower and move to
temp or dest tower.
3. no disk may be placed on the top of smaller disk.

def towers(n,src,temp,dest):
if n==1:
print(f"move disk {n} from {src} to {dest}")
return
towers(n-1,src,dest,temp)
print(f"move disk {n} from {src} to {dest}")
towers(n-1,temp,src,dest)

n=int(input())
towers(n,"S","T","D")

C:\test>py test.py
1
move disk 1 from S to D

C:\test>py test.py
2
move disk 1 from S to T
move disk 2 from S to D
move disk 1 from T to D
C:\test>py test.py
3
move disk 1 from S to D
move disk 2 from S to T
move disk 1 from D to T
move disk 3 from S to D
move disk 1 from T to S
move disk 2 from T to D
move disk 1 from S to D

C:\test>py test.py
4
move disk 1 from S to T
move disk 2 from S to D
move disk 1 from T to D
move disk 3 from S to T
move disk 1 from D to S
move disk 2 from D to T
move disk 1 from S to T
move disk 4 from S to D
move disk 1 from T to D
move disk 2 from T to S
move disk 1 from D to S
move disk 3 from T to D
move disk 1 from S to T
move disk 2 from S to D
move disk 1 from T to D

C:\test>py test.py
5
move disk 1 from S to D
move disk 2 from S to T
move disk 1 from D to T
move disk 3 from S to D
move disk 1 from T to S
move disk 2 from T to D
move disk 1 from S to D
move disk 4 from S to T
move disk 1 from D to T
move disk 2 from D to S
move disk 1 from T to S
move disk 3 from D to T
move disk 1 from S to D
move disk 2 from S to T
move disk 1 from D to T
move disk 5 from S to D
move disk 1 from T to S
move disk 2 from T to D
move disk 1 from S to D
move disk 3 from T to S
move disk 1 from D to T
move disk 2 from D to S
move disk 1 from T to S
move disk 4 from T to D
move disk 1 from S to D
move disk 2 from S to T
move disk 1 from D to T
move disk 3 from S to D
move disk 1 from T to S
move disk 2 from T to D
move disk 1 from S to D

C:\test>

14. find sub sets of given string


---------------------------------
"" null
a null, a
ab null, a, b, ab
abc null, a, b, c, ab, ac, bc, abc
n 2^n

def subsets(s,ans,index):
if index==len(s):
if len(ans)==0:
print("null")
else:
print(ans)
return
subsets(s,ans+s[index],index+1)
subsets(s,ans,index+1)

s=input()
ans=""
subsets(s,ans,0)

C:\test>py test.py
ab
ab
a
b
null

C:\test>py test.py
abc
abc
ab
ac
a
bc
b
c
null

15. find permuations of given string


-------------------------------------
a ------> a
ab -----> ab, ba
abc ----> abc, acb, bac, bca, cab, cba
n ------> n!

def fun(s,ans):
if len(s)==0:
print(ans)
return
for i in range(len(s)):
fun(s[:i]+s[i+1:],ans+s[i])

s=input()
fun(s,"")

16. grid ways


-------------
find number of ways to reach from (0,0) to (n-1,m-1) in nxm grid

allowed moves are right or down.

def gridways(i,j,n,m):
if i==n-1 and j==m-1:
return 1
elif i==n or j==m:
return 0
val1 = gridways(i+1,j,n,m)
val2 = gridways(i,j+1,n,m)
return val1 + val2

n = int(input())
m = int(input())
print(gridways(0,0,n,m))

C:\test>py test.py
2
2
2

C:\test>py test.py
3
3
6

C:\test>py test.py
4
4
20

Backtracking:
~~~~~~~~~~~~~
Introduction
------------
It is a method in which a solution is found by moving / searching through
large volume of data with some boundary condition.
Ex1: Number Locks
Ex2: Path Finding
Ex3: Sudoku Puzzle

Applications:
-------------
Number Locks
N-Queens
Sudoku
Rat in Maze etc

types of backtracking:
----------------------
1) decision based problems -------> Yes/No
2) optimized solution based problems ---> only one solution
3) enumeration based based problems ----> mutliple solutions

Backtracking with List


~~~~~~~~~~~~~~~~~~~~~~
def change(L,index,value):
if index==len(L):
print(L) #[1, 2, 3, 4, 5]
return
L[index] = value
change(L,index+1,value+1) #recursion
L[index] = L[index] - 2 #backtracking

L = [0, 0, 0, 0, 0]
print(L) #[0, 0, 0, 0, 0]
change(L,0,1)
print(L) #[-1, 0, 1, 2, 3]

C:\test>py test.py
[0, 0, 0, 0, 0]
[1, 2, 3, 4, 5]
[-1, 0, 1, 2, 3]

N-Queens Problem:
-----------------
def safe(L,row,col):
#vertical up
i=row-1
while i>=0:
if L[i][col]=='Q':
return False
i=i-1
#dia left up
i = row - 1
j = col - 1
while i>=0 and j>=0:
if L[i][j]=='Q':
return False
i=i-1
j=j-1
#dia right up
i = row - 1
j = col + 1
while i>=0 and j<len(L):
if L[i][j]=='Q':
return False
i=i-1
j=j+1
return True

def nqueens(L,row):
if row==len(L):
global c
c=c+1
printboard(L)
return
for j in range(len(L)):
if safe(L,row,j):
L[row][j] = 'Q'
nqueens(L,row+1) #recursion
L[row][j] = 'X' #backtracking

def printboard(L):
print("--- chess board ---")
for i in range(len(L)):
for j in range(len(L)):
print(L[i][j],end=' ')
print()
print("-------------------")

c=0
n = 5
L = [['X' for i in range(n)] for i in range(n)]
#printboard(L)
nqueens(L,0)
print("Number of sol:",c)

C:\test>py test.py
--- chess board ---
Q X X X X
X X Q X X
X X X X Q
X Q X X X
X X X Q X
-------------------
--- chess board ---
Q X X X X
X X X Q X
X Q X X X
X X X X Q
X X Q X X
-------------------
--- chess board ---
X Q X X X
X X X Q X
Q X X X X
X X Q X X
X X X X Q
-------------------
--- chess board ---
X Q X X X
X X X X Q
X X Q X X
Q X X X X
X X X Q X
-------------------
--- chess board ---
X X Q X X
Q X X X X
X X X Q X
X Q X X X
X X X X Q
-------------------
--- chess board ---
X X Q X X
X X X X Q
X Q X X X
X X X Q X
Q X X X X
-------------------
--- chess board ---
X X X Q X
Q X X X X
X X Q X X
X X X X Q
X Q X X X
-------------------
--- chess board ---
X X X Q X
X Q X X X
X X X X Q
X X Q X X
Q X X X X
-------------------
--- chess board ---
X X X X Q
X Q X X X
X X X Q X
Q X X X X
X X Q X X
-------------------
--- chess board ---
X X X X Q
X X Q X X
Q X X X X
X X X Q X
X Q X X X
-------------------
Number of sol: 10

sudoku puzzle
-------------
def safe(sudoku,row,col,d):
#col
for i in range(9):
if sudoku[i][col]==d:
return False
#row
for i in range(9):
if sudoku[row][i]==d:
return False
#grid
sr = (row//3)*3
sc = (col//3)*3
for i in range(sr,sr+3):
for j in range(sc,sc+3):
if sudoku[i][j]==d:
return False
return True

def sudokusolver(sudoku,row,col):
if row==9 and col==0:
return True
nextrow = row
nextcol = col + 1
if col+1==9:
nextrow = row + 1
nextcol = 0
if sudoku[row][col]!=0:
return sudokusolver(sudoku,nextrow,nextcol)
for d in range(1,10):
if safe(sudoku,row,col,d):
sudoku[row][col] = d #Recursion
if sudokusolver(sudoku,nextrow,nextcol):
return True
sudoku[row][col]=0 #backtracking
return False

def printsudoku(sudoku):
for i in range(len(sudoku)):
for j in range(len(sudoku)):
print(sudoku[i][j],end=' ')
print()

'''
sudoku = [
[4, 0, 7, 0, 6, 8, 0, 3, 0],
[9, 0, 0, 3, 0, 2, 0, 0, 0],
[0, 0, 0, 1, 0, 7, 2, 4, 0],
[1, 0, 2, 6, 8, 0, 4, 9, 0],
[5, 0, 8, 0, 0, 4, 0, 2, 0],
[0, 6, 0, 0, 0, 0, 0, 8, 0],
[8, 7, 0, 0, 0, 6, 3, 0, 4],
[3, 0, 0, 8, 0, 1, 7, 6, 2],
[0, 0, 6, 0, 0, 0, 0, 1, 9],
]
'''
sudoku = [
[0, 0, 7, 0, 0, 0, 8, 0, 0],
[3, 0, 6, 1, 8, 2, 7, 4, 5],
[0, 8, 0, 5, 3, 7, 0, 0, 0],
[6, 7, 2, 0, 0, 5, 9, 0, 0],
[0, 4, 9, 2, 0, 8, 0, 1, 0],
[0, 3, 0, 4, 0, 6, 0, 0, 7],
[7, 6, 0, 8, 0, 0, 4, 0, 9],
[9, 0, 8, 0, 0, 3, 0, 0, 0],
[0, 0, 0, 6, 0, 0, 3, 0, 0],
]
printsudoku(sudoku)
if sudokusolver(sudoku,0,0):
print("solution existed")
printsudoku(sudoku)
else:
print("solution not existed")

C:\test>py test.py
0 0 7 0 0 0 8 0 0
3 0 6 1 8 2 7 4 5
0 8 0 5 3 7 0 0 0
6 7 2 0 0 5 9 0 0
0 4 9 2 0 8 0 1 0
0 3 0 4 0 6 0 0 7
7 6 0 8 0 0 4 0 9
9 0 8 0 0 3 0 0 0
0 0 0 6 0 0 3 0 0
solution existed
1 5 7 9 6 4 8 3 2
3 9 6 1 8 2 7 4 5
2 8 4 5 3 7 1 9 6
6 7 2 3 1 5 9 8 4
5 4 9 2 7 8 6 1 3
8 3 1 4 9 6 2 5 7
7 6 3 8 5 1 4 2 9
9 2 8 7 4 3 5 6 1
4 1 5 6 2 9 3 7 8

Linked List Data Structure:


---------------------------
It is collection of nodes, which are connected with links (pointers).
There are two types of nodes are existed in linked list.

1) single node --> data field, pointer field pointing to next node
2) double node --> data field, pointer field pointing to next and prev
node
There are four types of linked lists are existed in data structures

1. single linked list


2. double linked list
3. circular single linked list
4. circular double linked list

The following are the various operations that can be perform on linked
list

01. creation of linked list


02. insertion at first location
03. insertion at last location
04. insertion at given location
05. sorted insertion in asc order
06. sorted insertion in desc order
07. displaying or traversing elements by using iteration
08. displaying or traversing elements by using recursion
09. size of linked list
10. middle element in the linked list
11. delete at first location
12. delete at last location
13. delete at given location
14. delete element
15. delete elements
16. search operation - I
17. search operation - II
18. search operation - III
19. search operation - IV
20. remove duplicates in sorted linked list
21. copy the list
22. reverse the list
23. nth node from the begining | ending
24. paliandrome linked list or not

1. single linked list


---------------------
01. creation of linked list ----------> ok
02. insertion at first location ------> ok
03. insertion at last location -------> ok
04. insertion at given location ------> ok
05. sorted insertion in asc order ----> ok
06. sorted insertion in desc order ---> ok
07. displaying or traversing elements by using iteration ----> ok
08. displaying or traversing elements by using recursion ----> ok
09. size of linked list -----------------> ok
10. middle element in the linked list ---> ok
11. delete at first location ------------> ok
12. delete at last location -------------> ok
13. delete at given location ------------> ok
14. delete element ----------------------> ok
15. delete elements ---------------------> ok
16. search operation - I ---------> 0k
17. search operation - II --------> ok
18. search operation - III -------> ok
19. search operation - IV --------> ok
20. remove duplicates in sorted linked list ----> ok
21. copy the list ------------------------------> ok
22. reverse the list----------------------------> ok
23. comapring two list objects -----------------> ok
23. nth node from the begining | ending --------> ok
24. paliandrome linked list or not -------------> ok

single linked list pdf file is existed in the folder

2. double linked list


---------------------
01. creation of linked list --------------------------------> OK
02. insertion at first location ----------------------------> OK
03. insertion at last location -----------------------------> OK
04. insertion at given location ----------------------------> OK
07. displaying or traversing elements by using iteration ---> OK
08. displaying or traversing elements by using recursion ---> OK
09. size of linked list ------------------------------------> OK
11. delete at first location -------------------------------> OK
12. delete at last location---------------------------------> OK
13. delete at given location -------------------------------> OK
16. search operation - I -----------------------------------> OK
17. search operation - II ----------------------------------> OK
21. copy the list ------------------------------------------> OK
22. reverse the list ---------------------------------------> OK

Ex:
---
class dll:

class node:

def __init__(self,data,next=None,prev=None):
self.data = data
self.next = next
self.prev = prev

def __init__(self):
self.head = None
self.tail = None
self.count = 0

def size(self):
return self.count

def isempty(self):
return self.count==0

#displaying elements in dll using iteration


def printlist(self):
if self.head==None:
print("list is empty")
return
currnode = self.head
while currnode!=None:
print(currnode.data,end=" <=> ")
currnode = currnode.next
print("None")
return

#displaying elements in dll using recursion


def printlistr(slef,temp):
if temp==None:
print("None")
return None
print(temp.data,end=" <=> ")
self.printlistr(temp.next)

#insert at first
def insertatfirst(self,data):
newnode = self.node(data,None,None)
self.count = self.count + 1
if self.head==None:
self.head = newnode
self.tail = newnode
return
self.head.prev = newnode
newnode.next = self.head
self.head = newnode
return

#insert at last
def insertatlast(self,data):
newnode = self.node(data,None,None)
self.count = self.count + 1
if self.head ==None:
self.head = newnode
self.tail = newnode
return
newnode.prev = self.tail
self.tail.next = newnode
self.tail = newnode
return

#insert at location
def insertatlocation(self,index,data):
if index<0 or index>self.count:
print("out of range")
return
newnode = self.node(data,None,None)
self.count = self.count + 1
if index==0:
self.head.prev=newnode
newnode.next=self.head
self.head=newnode
return
if index==self.count-1:
newnode.prev = self.tail
self.tail.next = newnode
self.tail = newnode
return
temp1 = self.head
temp2 = None
i=0
while temp1!=None and i<index:
temp2 = temp1
temp1 = temp1.next
i=i+1
temp2.next = newnode
newnode.prev = temp2
newnode.next = temp1
temp1.prev = newnode
return

#delete at first O(1)


def deleteatfirst(self):
if self.head==None:
print("list is empty")
return
self.count = self.count - 1
self.head = self.head.next
if self.head!=None:
self.head.prev = None

#delete at end O(1)


def deleteatlast(self):
if self.head==None:
print("list is empty")
return
self.count = self.count - 1
temp = self.tail.prev
temp.next = None
self.tail.prev = None
self.tail = temp
return

#delete from location


def deleteatlocation(self,index):
if self.head==None:
print("list is empty")
return
if index<0 or index>=self.count:
print("out of range")
return
if index==0:
self.count = self.count - 1
self.head = self.head.next
if self.head!=None:
self.head.prev = None
return
if index==self.count-1:
self.count = self.count - 1
temp = self.tail.prev
temp.next = None
self.tail.prev = None
self.tail = temp
return
i = 1
temp1 = self.head
while temp1.next!=None and i<=index:
if i==index:
temp1.next = temp1.next.next
temp2 = temp1.next
if temp2!=None:
temp2.prev = temp1
self.count = self.count - 1
return
temp2 = temp1
temp1 = temp1.next
i=i+1

#search 1
def search1(self,data):
currnode = self.head
while currnode!=None:
if currnode.data==data:
return True
currnode = currnode.next
return False

#search 2
def search2(self,data):
currnode = self.head
i=0
currnode = self.head
while currnode!=None:
if currnode.data == data:
return i
currnode = currnode.next
i=i+1
return -1

#copy list
def copylist(self):
headnode = None
tailnode = None
tempnode = None
currnode = self.head
if currnode==None:
return None
headnode = self.node(currnode.data,None,None)
tailnode = headnode
currnode = currnode.next
while currnode!=None:
tempnode = self.node(currnode.data,None,None)
tailnode.next = tempnode
tempnode.prev = tailnode
tailnode = tempnode
currnode = currnode.next
newlist = dll()
newlist.head = headnode
return newlist

#reverse
def reverse(self):
temp = None
currnode = self.head
while currnode!=None:
temp = currnode.prev
currnode.prev = currnode.next
currnode.next = temp
currnode = currnode.prev
if temp!=None:
self.head = temp.prev

list = dll()
list.insertatfirst(444)
list.insertatfirst(333)
list.insertatfirst(222)
list.insertatfirst(111)
list.printlist()
list.reverse()
list.printlist()

3. circular single linked list


------------------------------
creation of csll class
creation of node class
creation of constructor for node class
creation of constructor for csll class
size
insert at first
insert at last
print list
delete at first
delete at last

Ex:
---
class csll:

class node:
def __init__(self,data,next=None):
self.data = data
self.next = next

def __init__(self):
self.tail = None
self.count = 0

def size(self):
return self.count

#insert at first
def insertatfirst(self,data):
newnode = self.node(data,None)
self.count = self.count + 1
if self.tail==None:
self.tail = newnode
newnode.next=newnode
return
newnode.next = self.tail.next
self.tail.next = newnode
return

#insert at last
def insertatlast(self,data):
newnode = self.node(data,None)
self.count = self.count + 1
if self.tail==None:
self.tail = newnode
newnode.next = newnode
return
newnode.next = self.tail.next
self.tail.next = newnode
self.tail = newnode
return

#displaying list
def printlist(self):
if self.tail == None:
print("list is empty")
return
currnode = self.tail.next
while currnode!=self.tail:
print(currnode.data,end=" => ")
currnode = currnode.next
print(currnode.data)

#delete at first
def deleteatfirst(self):
if self.count==0:
print("list is empty")
return
self.count = self.count - 1
if self.tail == self.tail.next:
self.tail = None
return
self.tail.next = self.tail.next.next

#delete at last
def deleteatlast(self):
if self.count==0:
print("list is empty")
return
self.count=self.count-1
if self.tail == self.tail.next:
self.tail=None
return
currnode = self.tail.next
while currnode.next!=self.tail:
currnode = currnode.next
currnode.next = self.tail.next
self.tail = currnode

list = csll()
list.insertatfirst(10)
list.insertatfirst(5)
list.insertatfirst(4)
list.insertatlast(20)
list.insertatlast(30)
list.printlist()
list.deleteatlast()
list.printlist()

4. circular double linked list


------------------------------
creation of cdll class
creation of node class
creation of constructor for node class
creation of constructor for cdll class
size
insert at first
insert at last
print list
delete at first
delete at last

class cdll:

class node:

def __init__(self,data,next=None,prev=None):
self.data = data
self.next = next
self.prev = prev

def __init__(self):
self.head = None
self.tail = None
self.count = 0

def size(self):
return self.count
#insert at first
def inseratfirst(self,data):
newnode = self.node(data,None,None)
self.count = self.count + 1
if self.tail==None:
self.tail = newnode
self.head = newnode
newnode.next = newnode
newnode.prev = newnode
return
newnode.next = self.head
newnode.prev = self.head.prev
self.head.prev = newnode
newnode.prev.next = newnode
self.head = newnode
return

#print list
def printlist(self):
if self.tail==None:
print("list is empty..")
return
currnode = self.tail.next
while currnode!=self.tail:
print(currnode.data,end=" => ")
currnode = currnode.next
print(currnode.data)

#insert at last
def inseratlast(self,data):
newnode = self.node(data,None,None)
self.count = self.count + 1
if self.tail == None:
self.tail = newnode
self.head = newnode
newnode.next = newnode
newnode.prev = newnode
return
newnode.next = self.tail.next
newnode.prev = self.tail
self.tail.next = newnode
newnode.next.prev = newnode
self.tail = newnode

#delete at first
def deleteatfirst(self):
if self.count==0:
print("list is empty...")
return
self.count = self.count - 1
if self.count==0:
self.tail = None
self.head = None
return
temp = self.head.next
temp.prev = self.tail
self.tail.next = temp
self.head = temp

#delete at last
def deleteatlast(self):
if self.count==0:
print("list is empty")
return
self.count = self.count - 1
if self.count == 0:
self.tail = None
self.head = None
return
temp = self.tail.prev
temp.next = self.head
self.head.prev = temp
self.tail = temp

list = cdll()
list.inseratfirst(1)
list.inseratfirst(2)
list.inseratfirst(3)
list.inseratfirst(4)
list.printlist()
list.deleteatlast()
list.printlist()

Sorting Algorithms:
~~~~~~~~~~~~~~~~~~~
Arranging the elements / objects in ascending order or descending is
called as sorting. we can do this sorting using predefined methods or we
can implement our own algorithms.

predefined -----> sort() and sorted()


userdefined ----> bubble sort, selection sort, quick, merge etc...

sort() method:
--------------
==> it is applicable only for list objects.
==> if we want to perform in asc order ------> L.sort()
==> if we want to perform in desc order -----> L.sort(reverse=True)
==> if we want to perform sorting based on our kye ---> L.sort(key=fun)

Ex: Sorting integer objects in ascending order


----------------------------------------------
L = [10, 14, 12, 15, 13, 11]
print(L)
L.sort()
print(L)
C:\test>py test.py
[10, 14, 12, 15, 13, 11]
[10, 11, 12, 13, 14, 15]

C:\test>

Ex: Sorting integer objects in desending order


----------------------------------------------
L = [10, 14, 12, 15, 13, 11]
print(L)
L.sort(reverse=True)
print(L)

C:\test>py test.py
[10, 14, 12, 15, 13, 11]
[15, 14, 13, 12, 11, 10]

C:\test>

Ex: Sorting string objects in ascending order


----------------------------------------------
L = ["HHH","BBB","PPP","CCC","KKK","AAA"]
print(L)
L.sort()
print(L)

C:\test>py test.py
['HHH', 'BBB', 'PPP', 'CCC', 'KKK', 'AAA']
['AAA', 'BBB', 'CCC', 'HHH', 'KKK', 'PPP']

Ex: Sorting string objects in descending order


----------------------------------------------
L = ["HHH","BBB","PPP","CCC","KKK","AAA"]
print(L)
L.sort(reverse=True)
print(L)

C:\test>py test.py
['HHH', 'BBB', 'PPP', 'CCC', 'KKK', 'AAA']
['PPP', 'KKK', 'HHH', 'CCC', 'BBB', 'AAA']

Ex: sorting string objects based on their length in asc order


-------------------------------------------------------------
def fun(s):
return len(s)

L = ["Ram","Prabas","Charan","Tarak","Arjun"]
print(L)
L.sort(key=fun)
print(L)

C:\test>py test.py
['Ram', 'Prabas', 'Charan', 'Tarak', 'Arjun']
['Ram', 'Tarak', 'Arjun', 'Prabas', 'Charan']
Ex: sorting string objects based on their length in desc order
--------------------------------------------------------------
def fun(s):
return len(s)

L = ["Ram","Prabas","Charan","Tarak","Arjun"]
print(L)
L.sort(key=fun,reverse=True)
print(L)

C:\test>py test.py
['Ram', 'Prabas', 'Charan', 'Tarak', 'Arjun']
['Prabas', 'Charan', 'Tarak', 'Arjun', 'Ram']

Ex: sort the student objects based on htno number in asc order
--------------------------------------------------------------
class student:

def __init__(self,htno,name,percentage):
self.htno = htno
self.name = name
self.percentage = percentage

def __str__(self):
return f"({self.htno}, {self.name}, {self.percentage})"

s1 = student(111,"Prakash",56.78)
s2 = student(333,"Sekhar",76.13)
s3 = student(222,"Sarath",98.34)
s4 = student(666,"Kiran",87.34)
s5 = student(444,"Durga",66.77)

def fun(s):
return s.htno

L = [s1, s2, s3, s4, s5]


print("BEFORE SORTING")
for i in L:
print(i)

L.sort(key=fun)

print("AFTER SORTING")
for i in L:
print(i)

C:\test>py test.py
BEFORE SORTING
(111, Prakash, 56.78)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(444, Durga, 66.77)
AFTER SORTING
(111, Prakash, 56.78)
(222, Sarath, 98.34)
(333, Sekhar, 76.13)
(444, Durga, 66.77)
(666, Kiran, 87.34)

Ex: sort the student objects based on htno number in desc order
--------------------------------------------------------------
class student:

def __init__(self,htno,name,percentage):
self.htno = htno
self.name = name
self.percentage = percentage

def __str__(self):
return f"({self.htno}, {self.name}, {self.percentage})"

s1 = student(111,"Prakash",56.78)
s2 = student(333,"Sekhar",76.13)
s3 = student(222,"Sarath",98.34)
s4 = student(666,"Kiran",87.34)
s5 = student(444,"Durga",66.77)

def fun(s):
return s.htno

L = [s1, s2, s3, s4, s5]


print("BEFORE SORTING")
for i in L:
print(i)

L.sort(key=fun,reverse=True)

print("AFTER SORTING")
for i in L:
print(i)

C:\test>py test.py
BEFORE SORTING
(111, Prakash, 56.78)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(444, Durga, 66.77)
AFTER SORTING
(666, Kiran, 87.34)
(444, Durga, 66.77)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(111, Prakash, 56.78)

C:\test>
Ex: sort the student objects based on name in asc order
-------------------------------------------------------
class student:

def __init__(self,htno,name,percentage):
self.htno = htno
self.name = name
self.percentage = percentage

def __str__(self):
return f"({self.htno}, {self.name}, {self.percentage})"

s1 = student(111,"Prakash",56.78)
s2 = student(333,"Sekhar",76.13)
s3 = student(222,"Sarath",98.34)
s4 = student(666,"Kiran",87.34)
s5 = student(444,"Durga",66.77)

def fun(s):
return s.name

L = [s1, s2, s3, s4, s5]


print("BEFORE SORTING")
for i in L:
print(i)

L.sort(key=fun)

print("AFTER SORTING")
for i in L:
print(i)

C:\test>py test.py
BEFORE SORTING
(111, Prakash, 56.78)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(444, Durga, 66.77)
AFTER SORTING
(444, Durga, 66.77)
(666, Kiran, 87.34)
(111, Prakash, 56.78)
(222, Sarath, 98.34)
(333, Sekhar, 76.13)

Ex: sort the student objects based on name in desc order


-------------------------------------------------------
class student:

def __init__(self,htno,name,percentage):
self.htno = htno
self.name = name
self.percentage = percentage

def __str__(self):
return f"({self.htno}, {self.name}, {self.percentage})"

s1 = student(111,"Prakash",56.78)
s2 = student(333,"Sekhar",76.13)
s3 = student(222,"Sarath",98.34)
s4 = student(666,"Kiran",87.34)
s5 = student(444,"Durga",66.77)

def fun(s):
return s.name

L = [s1, s2, s3, s4, s5]


print("BEFORE SORTING")
for i in L:
print(i)

L.sort(key=fun,reverse=True)

print("AFTER SORTING")
for i in L:
print(i)

C:\test>py test.py
BEFORE SORTING
(111, Prakash, 56.78)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(444, Durga, 66.77)
AFTER SORTING
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(111, Prakash, 56.78)
(666, Kiran, 87.34)
(444, Durga, 66.77)

Ex: sort the student objects based on percentage of marks in asc order
----------------------------------------------------------------------
class student:

def __init__(self,htno,name,percentage):
self.htno = htno
self.name = name
self.percentage = percentage

def __str__(self):
return f"({self.htno}, {self.name}, {self.percentage})"

s1 = student(111,"Prakash",56.78)
s2 = student(333,"Sekhar",76.13)
s3 = student(222,"Sarath",98.34)
s4 = student(666,"Kiran",87.34)
s5 = student(444,"Durga",66.77)

def fun(s):
return s.percentage

L = [s1, s2, s3, s4, s5]


print("BEFORE SORTING")
for i in L:
print(i)

L.sort(key=fun)

print("AFTER SORTING")
for i in L:
print(i)

C:\test>py test.py
BEFORE SORTING
(111, Prakash, 56.78)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(444, Durga, 66.77)
AFTER SORTING
(111, Prakash, 56.78)
(444, Durga, 66.77)
(333, Sekhar, 76.13)
(666, Kiran, 87.34)
(222, Sarath, 98.34)

Ex: sort the student objects based on percentage of marks in desc order
-----------------------------------------------------------------------
class student:

def __init__(self,htno,name,percentage):
self.htno = htno
self.name = name
self.percentage = percentage

def __str__(self):
return f"({self.htno}, {self.name}, {self.percentage})"

s1 = student(111,"Prakash",56.78)
s2 = student(333,"Sekhar",76.13)
s3 = student(222,"Sarath",98.34)
s4 = student(666,"Kiran",87.34)
s5 = student(444,"Durga",66.77)

def fun(s):
return s.percentage

L = [s1, s2, s3, s4, s5]


print("BEFORE SORTING")
for i in L:
print(i)

L.sort(key=fun,reverse=True)

print("AFTER SORTING")
for i in L:
print(i)

C:\test>py test.py
BEFORE SORTING
(111, Prakash, 56.78)
(333, Sekhar, 76.13)
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(444, Durga, 66.77)
AFTER SORTING
(222, Sarath, 98.34)
(666, Kiran, 87.34)
(333, Sekhar, 76.13)
(444, Durga, 66.77)
(111, Prakash, 56.78)

sorted() function:
------------------
it sorts the given data in asc / desc order and returns the data in the
form of a list, it wn't modify original data.

Ex:
---
L = [1, 5, 2, 4, 3]
print(L)
NL=sorted(L)
print(L)
print(NL)

C:\test>py test.py
[1, 5, 2, 4, 3]
[1, 5, 2, 4, 3]
[1, 2, 3, 4, 5]

Ex:
---
L = [1, 5, 2, 4, 3]
print(L)
NL=sorted(L,reverse=True)
print(L)
print(NL)

C:\test>py test.py
[1, 5, 2, 4, 3]
[1, 5, 2, 4, 3]
[5, 4, 3, 2, 1]
bubble sort
-----------
==> sort the data in asc or desc order.
==> stable sort.
==> first we will compare first element with second element, if largest
came then we have to swap.
==> we need to repeat this step for n times.

Ex: Implement bubble sort alg to sort the data in asc order
-----------------------------------------------------------
import random

#O(n2)
def bubblesort(L):
n=len(L)
for i in range(n-1):
for j in range(n-i-1):
if L[j]>L[j+1]:
L[j],L[j+1]=L[j+1],L[j]

L = []
for i in range(10):
L.append(random.randint(0,99))

print(L)
bubblesort(L)
print(L)

C:\test>py test.py
[2, 72, 99, 10, 31, 39, 35, 0, 85, 48]
[0, 2, 10, 31, 35, 39, 48, 72, 85, 99]

Ex:Implement bubble sort alg to sort the data in desc order


-----------------------------------------------------------
import random

#O(n2)
def bubblesort(L):
n=len(L)
for i in range(n-1):
for j in range(n-i-1):
if L[j]<L[j+1]:
L[j],L[j+1]=L[j+1],L[j]

L = []
for i in range(10):
L.append(random.randint(0,99))

print(L)
bubblesort(L)
print(L)
C:\test>py test.py
[18, 45, 68, 67, 2, 31, 64, 61, 86, 20]
[86, 68, 67, 64, 61, 45, 31, 20, 18, 2]

selection sort
--------------
==> select an element and fix the position in its place.
==> first find min/max element move to the first or last location.

Ex:
---
import random

#O(n2)
def selectionsort(L):
n=len(L)
for i in range(n-1):
minIndex = i
for j in range(i+1,n):
if L[j] < L[minIndex]:
minIndex = j
L[i],L[minIndex]=L[minIndex],L[i]

L = []
for i in range(10):
L.append(random.randint(0,99))

print(L)
selectionsort(L)
print(L)

C:\test>py test.py
[77, 6, 5, 26, 21, 0, 34, 13, 64, 7]
[0, 5, 6, 7, 13, 21, 26, 34, 64, 77]

Ex:
import random

#O(n2)
def selectionsort(L):
n=len(L)
for i in range(n-1):
maxIndex = i
for j in range(i+1,n):
if L[j] > L[maxIndex]:
maxIndex = j
L[i],L[maxIndex]=L[maxIndex],L[i]

L = []
for i in range(10):
L.append(random.randint(0,99))

print(L)
selectionsort(L)
print(L)

C:\test>py test.py
[65, 20, 9, 98, 33, 75, 54, 85, 93, 26]
[98, 93, 85, 75, 65, 54, 33, 26, 20, 9]

Insertion sort
--------------
Ex:
---
import random

#O(n2)
def insertionsort(L):
n=len(L)
for i in range(1,n):
x = L[i]
j = i-1
while j>=0 and x<L[j]:
L[j+1] = L[j]
j=j-1
L[j+1]=x

L = []
for i in range(10):
L.append(random.randint(0,99))

print(L)
insertionsort(L)
print(L)

C:\test>py test.py
[64, 89, 84, 83, 59, 99, 50, 70, 65, 48]
[48, 50, 59, 64, 65, 70, 83, 84, 89, 99]

Ex:
---
import random

#O(n2)
def insertionsort(L):
n=len(L)
for i in range(1,n):
x = L[i]
j = i-1
while j>=0 and x>L[j]:
L[j+1] = L[j]
j=j-1
L[j+1]=x

L = []
for i in range(10):
L.append(random.randint(0,99))

print(L)
insertionsort(L)
print(L)

C:\test>py test.py
[52, 78, 52, 14, 78, 58, 47, 20, 2, 78]
[78, 78, 78, 58, 52, 52, 47, 20, 14, 2]

counting sort
-------------
def countingsort(L,k):
output = [0] * len(L)
count = [0] * k
for i in L:
count[i] = count[i] + 1
for i in range(1,k):
count[i] = count[i] + count[i-1]
for i in reversed(L):
output[count[i]-1] = i
count[i] = count[i]-1
for i in range(len(L)):
L[i] = output[i]

L = [1, 4, 4, 1, 0, 1]
print(L)
countingsort(L,5)
print(L)

C:\test>py test.py
[1, 4, 4, 1, 0, 1]
[0, 1, 1, 1, 4, 4]

C:\test>

radix sort
----------
Ex:
---
def countingsort(L,pos):
output = [0]*len(L)
count = [0] * 10
for i in range(0,len(L)):
index = (L[i]//pos)%10
count[index]=count[index]+1
for i in range(1,10):
count[i] = count[i] + count[i-1]
i=len(L)-1
while i>=0:
index= (L[i]//pos)%10
output[count[index]-1] = L[i]
count[index]=count[index]-1
i=i-1
for i in range(0,len(L)):
L[i] = output[i]

def radixsort(L):
m = max(L)
pos = 1
while m//pos>0:
countingsort(L,pos)
pos=pos*10

L = [319, 212, 6, 18, 100, 55]


print(L)
radixsort(L)
print(L)

C:\test>py test.py
[319, 212, 6, 18, 100, 55]
[6, 18, 55, 100, 212, 319]

divide and conquer algorithms


-----------------------------
divide and conquer is a method, which divides a big problem into small
small individual sub problem. we can find solution for those sub problems
and finally we can combine solutions of theose sub problems to get
solution for original problem.

Ex:
Merge Sort
Quick Sort
Binary Search
etc

Ex1:
----
def merge(l1,l2):
l3 = l1 + l2
l3.sort()
return l3

a = [10, 15, 20]


b = [5, 6, 7, 30]
print(a)
print(b)
print(merge(a,b))

C:\test>py test.py
[10, 15, 20]
[5, 6, 7, 30]
[5, 6, 7, 10, 15, 20, 30]

Ex2:
----
def merge(l1,l2):
l3 = []
m,n = len(l1),len(l2)
i,j=0,0
while i<m and j<n:
if l1[i]<l2[j]:
l3.append(l1[i])
i=i+1
else:
l3.append(l2[j])
j=j+1

while i<m:
l3.append(l1[i])
i=i+1

while j<n:
l3.append(l2[j])
j=j+1

return l3

a = [10, 15, 20]


b = [5, 6, 7, 30]
print(a)
print(b)
print(merge(a,b))

C:\test>py test.py
[10, 15, 20]
[5, 6, 7, 30]
[5, 6, 7, 10, 15, 20, 30]

Ex:
---
def mergesort(L,lindex,rindex):
if rindex>lindex:
mid = (lindex + rindex)//2
mergesort(L,lindex,mid)
mergesort(L,mid+1,rindex)
merge(L,lindex,mid,rindex)

def merge(L,low,mid,high):
l = L[low:mid+1]
r = L[mid+1:high+1]
i=0
j=0
k=low
while i<len(l) and j<len(r):
if l[i]<r[j]:
L[k] = l[i]
i=i+1
k=k+1
else:
L[k] = r[j]
j=j+1
k=k+1
while i<len(l):
L[k]=l[i]
i=i+1
k=k+1
while j<len(r):
L[k]=r[j]
j=j+1
k=k+1

L = [4, 6, 1, 9, 2, 7, 3, 8, 5]
print(L)
mergesort(L,0,len(L)-1)
print(L)

C:\test>py test.py
[4, 6, 1, 9, 2, 7, 3, 8, 5]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Quick Sort:
-----------
def quicksort(l,low,high):
if high<=low:
return
pivot = l[low]
start = low
end = high
while low<high:
while l[low] <= pivot and low<high:
low = low + 1
while l[high] > pivot and low<=high:
high = high - 1
if low < high:
l[high],l[low] = l[low],l[high]
l[high],l[start] = l[start],l[high]
quicksort(l,start,high-1)
quicksort(l,high+1,end)

L = [4, 6, 1, 9, 2, 7, 3, 8, 5]
print(L)
quicksort(L,0,len(L)-1)
print(L)

C:\test>py test.py
[4, 6, 1, 9, 2, 7, 3, 8, 5]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
SEARCHING:
~~~~~~~~~~
searching is the process of fidning an item / object / element in a
collection of items the item may be keyword in file, book in lib, student
record in db, an obj in the list.

The following are the two ways of perfomring search operation

1. linear search
2. binary search

Ex: Implement linear search algorithm first occurrence


------------------------------------------------------
import random

def linearsearch(L,key):
for i in range(len(L)):
if key==L[i]:
return i
return -1

L = []
for i in range(10):
L.append(random.randint(1,9))
print(L)
key = int(input("Enter element to search: "))
print(linearsearch(L,key))

C:\test>py test.py
[3, 4, 1, 4, 4, 9, 3, 3, 5, 2]
Enter element to search: 4
1

C:\test>py test.py
[7, 5, 6, 9, 6, 7, 5, 6, 2, 8]
Enter element to search: 6
2

C:\test>py test.py
[7, 1, 2, 1, 1, 1, 9, 4, 6, 1]
Enter element to search: 3
-1

Ex: Implement linear search algorithm last occurrence


------------------------------------------------------
import random

def linearsearch(L,key):
for i in range(len(L)-1,0,-1):
if key==L[i]:
return i
return -1

L = []
for i in range(10):
L.append(random.randint(1,9))
print(L)
key = int(input("Enter element to search: "))
print(linearsearch(L,key))

C:\test>py test.py
[1, 1, 7, 3, 2, 8, 2, 3, 5, 3]
Enter element to search: 3
9

C:\test>py test.py
[1, 5, 8, 2, 5, 1, 2, 6, 4, 6]
Enter element to search: 5
4

C:\test>py test.py
[1, 9, 2, 3, 1, 8, 8, 8, 5, 7]
Enter element to search: 4
-1

Ex: Implement linear search algorithm first & second occurrence


---------------------------------------------------------------
import random

def linearsearch(L,key):
TL = []
for i in range(len(L)):
if key==L[i]:
TL.append(i)
return TL[0:2]

L = []
for i in range(10):
L.append(random.randint(1,9))
print(L)
key = int(input("Enter element to search: "))
print(linearsearch(L,key))

C:\test>py test.py
[9, 9, 1, 3, 8, 3, 6, 3, 6, 4]
Enter element to search: 9
[0, 1]

C:\test>py test.py
[9, 6, 3, 5, 1, 9, 9, 7, 8, 3]
Enter element to search: 9
[0, 5]

Ex: Implement linear search algorithm all occurrences


-----------------------------------------------------
import random

def linearsearch(L,key):
TL = []
for i in range(len(L)):
if key==L[i]:
TL.append(i)
return TL

L = []
for i in range(10):
L.append(random.randint(1,9))
print(L)
key = int(input("Enter element to search: "))
print(linearsearch(L,key))

C:\test>py test.py
[6, 2, 1, 3, 3, 6, 2, 6, 9, 1]
Enter element to search: 1
[2, 9]

C:\test>py test.py
[6, 6, 5, 6, 6, 2, 1, 1, 3, 6]
Enter element to search: 6
[0, 1, 3, 4, 9]

C:\test>py test.py
[9, 5, 4, 7, 1, 6, 8, 4, 7, 7]
Enter element to search: 5
[1]

C:\test>py test.py
[1, 7, 7, 6, 4, 5, 2, 8, 7, 5]
Enter element to search: 9
[]

Ex: Implementation of binary search using loops


-----------------------------------------------
import random

def binarysearch(L,key):
low = 0
high = len(L)-1
while low<=high:
mid = (low+high)//2
if key==L[mid]:
return mid
elif key<L[mid]:
high = mid-1
else:
low = mid+1
return -1

L = []
for i in range(10):
L.append(random.randint(10,99))
L.sort()
print(L)
key = int(input("Enter element to search: "))
print(binarysearch(L,key))

C:\test>py test.py
[16, 21, 27, 58, 61, 72, 90, 93, 95, 99]
Enter element to search: 90
6

C:\test>py test.py
[15, 23, 25, 31, 34, 57, 66, 69, 71, 75]
Enter element to search: 80
-1

Ex: Implementation of binary search using recursion


----------------------------------------------------
import random

def binarysearch(L,key,low,high):
if low>high:
return -1
mid = (low+high)//2
if key==L[mid]:
return mid
elif key<L[mid]:
return binarysearch(L,key,low,mid-1)
else:
return binarysearch(L,key,mid+1,high)

L = []
for i in range(10):
L.append(random.randint(10,99))
L.sort()
print(L)
key = int(input("Enter element to search: "))
print(binarysearch(L,key,0,len(L)-1))

C:\test>py test.py
[25, 30, 32, 32, 49, 56, 58, 68, 69, 76]
Enter element to search: 58
6

C:\test>py test.py
[15, 23, 23, 49, 57, 66, 71, 87, 89, 93]
Enter element to search: 15
0

C:\test>py test.py
[21, 44, 55, 58, 64, 65, 67, 75, 76, 98]
Enter element to search: 98
9

C:\test>py test.py
[14, 22, 26, 27, 42, 71, 73, 77, 84, 97]
Enter element to search: 99
-1

Ex: Implementation of binary search to search for an element in 1st half


------------------------------------------------------------------------
import random

def binarysearch(L,key,low,high):
if low>high:
return -1
mid = (low+high)//2
if key==L[mid]:
return mid
elif key<L[mid]:
return binarysearch(L,key,low,mid-1)
else:
return binarysearch(L,key,mid+1,high)

L = []
for i in range(10):
L.append(random.randint(10,99))
L.sort()
print(L)
key = int(input("Enter element to search: "))
print(binarysearch(L,key,0,len(L)//2-1))

C:\test>py test.py
[19, 20, 45, 47, 53, 55, 61, 69, 76, 77]
Enter element to search: 76
-1

C:\test>py test.py
[10, 15, 16, 25, 38, 40, 43, 65, 90, 95]
Enter element to search: 38
4

C:\test>py test.py
[12, 32, 32, 55, 55, 58, 80, 81, 87, 99]
Enter element to search: 58
-1

Ex: Implementation of binary search to search for an element in 2nd half


------------------------------------------------------------------------
import random

def binarysearch(L,key,low,high):
if low>high:
return -1
mid = (low+high)//2
if key==L[mid]:
return mid
elif key<L[mid]:
return binarysearch(L,key,low,mid-1)
else:
return binarysearch(L,key,mid+1,high)

L = []
for i in range(10):
L.append(random.randint(10,99))
L.sort()
print(L)
key = int(input("Enter element to search: "))
print(binarysearch(L,key,len(L)//2,len(L)-1))

C:\test>py test.py
[17, 22, 25, 28, 36, 41, 47, 60, 73, 87]
Enter element to search: 17
-1

C:\test>py test.py
[12, 12, 14, 17, 26, 51, 59, 65, 81, 93]
Enter element to search: 51
5

C:\test>py test.py
[11, 17, 25, 42, 44, 45, 56, 60, 61, 73]
Enter element to search: 44
-1

C:\test>py test.py
[14, 27, 47, 50, 51, 51, 54, 66, 91, 96]
Enter element to search: 96
9

Ex: Implementation of binary search to search for an element in range


---------------------------------------------------------------------
import random

def binarysearch(L,key,low,high):
if low>high:
return -1
mid = (low+high)//2
if key==L[mid]:
return mid
elif key<L[mid]:
return binarysearch(L,key,low,mid-1)
else:
return binarysearch(L,key,mid+1,high)

L = []
for i in range(10):
L.append(random.randint(10,99))
L.sort()
print(L)
key = int(input("Enter element to search: "))
start = int(input("Enter start value: "))
end = int(input("Enter end value: "))
print(binarysearch(L,key,start,end))

C:\test>py test.py
[34, 35, 38, 53, 59, 75, 76, 88, 89, 91]
Enter element to search: 3
Enter start value: 3
Enter end value: 5
-1

C:\test>py test.py
[16, 19, 21, 36, 56, 58, 68, 72, 94, 99]
Enter element to search: 56
Enter start value: 3
Enter end value: 5
4

C:\test>py test.py
[14, 15, 25, 57, 63, 70, 90, 93, 95, 96]
Enter element to search: 3
Enter start value: 3
Enter end value: 5
-1

C:\test>py test.py
[13, 24, 26, 46, 56, 59, 60, 66, 70, 96]
Enter element to search: 96
Enter start value: 3
Enter end value: 7
-1

C:\test>py test.py
[11, 25, 28, 56, 61, 65, 71, 73, 74, 95]
Enter element to search: 25
Enter start value: 1
Enter end value: 4
1

stack data structure:


~~~~~~~~~~~~~~~~~~~~~
introduction:
-------------
==> a stack is linear data structure
==> memory allocation for the objects are continues memory location
==> Last - In - First - Out
==> the item which is inserted at last will be the item remove first.

Ex:
stack of plats
disks in the rack
function calls are stored in stack
web page navigation
parenthesis balancing
reversing items
expressions conversion
expr eval
undo/redo
forward/backward
etc

operations that can be performed on stack


-----------------------------------------
The following are the very common operations that can be performed on
stack

1. push ----> inserting an element into stack


2. pop -----> deleting an element from stack
3. peek ----> returing top most element
4. size ----> returns the size of stack
5. isempty -> return True if stack is empty false
6. display -> it disply each and every element in a stack

implementation of stack
-----------------------
container and top

when an obj is inserted into stack, top variable will be increment by one
unit
when an obj is deleted from stack, top variable will be decremented by
one unit

Implementation of stack by uisng List


Implementation of stack by using collections.deque
Implementation of stack using user defined methods

Implementation of stack by uisng List


-------------------------------------
push -----> L.append(obj)
pop ------> L.pop()

L = []
print(L)
L.append(111)
L.append(222)
L.append(333)
L.append(444)
L.append(555)
print(L) #[111, 222, 333, 444, 555]
print(L[-1]) #555
print(L) #[111, 222, 333, 444, 555]
L.pop()
print(L) #[111, 222, 333, 444]
print(len(L)) #4
print(len(L)==0) #False

C:\test>py test.py
[]
[111, 222, 333, 444, 555]
555
[111, 222, 333, 444, 555]
[111, 222, 333, 444]
4
False

Implementation of stack by using collections.deque


--------------------------------------------------
from collections import deque
stack = deque()
print(stack) #[]
stack.append(10)
stack.append(20)
stack.append(30)
stack.append(40)
stack.append(50)
stack.append(60)
print(stack) #[10, 20, 30, 40, 50, 60]
print(stack[-1]) #60
stack.pop()
print(stack) #[10, 20, 30, 40, 50]

C:\test>py test.py
deque([])
deque([10, 20, 30, 40, 50, 60])
60
deque([10, 20, 30, 40, 50])

Implementation of stack using user defined methods own list


-----------------------------------------------------------
class stack:

def __init__(self):
self.s = []

def isempty(self):
return len(self.s)==0

def size(self):
return len(self.s)

def push(self,data):
self.s.append(data)

def pop(self):
return self.s.pop()

def display(self):
print(self.s)

def peek(self):
return self.s[-1]

s = stack()
s.push(10)
s.push(20)
s.push(30)
s.display()
print(s.peek())#30
print(s.pop()) #30
s.display() #[10,20]

C:\test>py test.py
[10, 20, 30]
30
30
[10, 20]

C:\test>

Implementation of stack using linked list


-----------------------------------------
class stack:

class node:

def __init__(self,data,next=None):
self.data = data
self.next = next

def __init__(self):
self.head = None
self.count = 0

def isempty(self):
return self.count==0

def size(self):
return self.count

def peek(self):
if self.isempty():
print("list is empty")
return
return self.head.data

def push(self,data):
self.head = self.node(data,self.head)
self.count = self.count + 1

def pop(self):
if self.isempty():
print("stack is empty")
return
val = self.head.data
self.count = self.count - 1
self.head = self.head.next
return val
def display(self):
if self.isempty():
print("list is empty")
return
temp = self.head
while temp!=None:
print(temp.data,end=" ")
temp = temp.next
print()

s = stack()
s.push(100)
s.push(200)
s.push(300)
s.display()
print(s.pop()) #300
s.display()
print(s.peek()) #200
print(s.isempty()) #True
print(s.size()) #2

C:\test>py test.py
300 200 100
300
200 100
200
False
2

Stack.py
--------
class stack:

class node:

def __init__(self,data,next=None):
self.data = data
self.next = next

def __init__(self):
self.head = None
self.count = 0

def isempty(self):
return self.count==0

def size(self):
return self.count

def peek(self):
if self.isempty():
print("list is empty")
return
return self.head.data

def push(self,data):
self.head = self.node(data,self.head)
self.count = self.count + 1

def pop(self):
if self.isempty():
print("stack is empty")
return
val = self.head.data
self.count = self.count - 1
self.head = self.head.next
return val

def display(self):
if self.isempty():
print("list is empty")
return
temp = self.head
while temp!=None:
print(temp.data,end=" ")
temp = temp.next
print()

stack to hold student objects


-----------------------------
from stack import *

class student:

def __init__(self,sid,sname):
self.sid = sid
self.sname = sname

def __str__(self):
return f"({self.sid}, {self.sname})"

s1 = student(1,"AAA")
s2 = student(2,"BBB")
s3 = student(5,"UUU")
s4 = student(3,"WWW")
s5 = student(4,"CCC")

stk = stack()
stk.push(s1)
stk.push(s2)
stk.push(s3)
stk.push(s4)
stk.push(s5)
stk.display()
print(stk.pop())
stk.display()

sorted insertion in stack


-------------------------
from stack import *

def sortedinsert(s,data):
if s.isempty() or data > s.peek():
s.push(data)
else:
temp = s.pop()
sortedinsert(s,data)
s.push(temp)

s = stack()
s.push(111)
s.push(444)
s.push(666)
s.display()
sortedinsert(s,555)
s.display()
sortedinsert(s,222)
s.display()

C:\test>py test.py
666 444 111
666 555 444 111
666 555 444 222 111

sorting stack elements


----------------------
from stack import *

def sortedinsert(s,data):
if s.isempty() or data > s.peek():
s.push(data)
else:
temp = s.pop()
sortedinsert(s,data)
s.push(temp)

def sortstack(s):
if not s.isempty():
temp = s.pop()
sortstack(s)
sortedinsert(s,temp)
s = stack()
s.push(10)
s.push(40)
s.push(60)
s.push(30)
s.push(50)
s.push(20)
s.display()
sortstack(s)
s.display()

C:\test>py test.py
20 50 30 60 40 10
60 50 40 30 20 10

bottom insert in the stack


--------------------------
from stack import *

def bottominsert(s,data):
if s.isempty():
s.push(data)
else:
temp = s.pop()
bottominsert(s,data)
s.push(temp)

s = stack()
s.push(444)
s.push(111)
s.push(222)
s.push(333)
s.display() #333 222 111 444
bottominsert(s,999)
s.display() #333 222 111 444 999

reverse stack
-------------
from stack import *

def bottominsert(s,data):
if s.isempty():
s.push(data)
else:
temp = s.pop()
bottominsert(s,data)
s.push(temp)

def reversestack(s):
if not s.isempty():
temp = s.pop()
reversestack(s)
bottominsert(s,temp)

s = stack()
s.push(444)
s.push(111)
s.push(222)
s.push(333)
s.display() #333 222 111 444
reversestack(s)
s.display() #444 111 222 333

reverse a stack by using queue


------------------------------
from queue import *
from stack import *

def reversestack(s):
q = Queue(maxsize=5)
while not s.isempty():
q.put(s.pop())
while not q.empty():
s.push(q.get())

s = stack()
s.push(11)
s.push(22)
s.push(33)
s.display() #33 22 11
reversestack(s)
s.display() #11 22 33

reverse K elements in stack


---------------------------
from queue import *
from stack import *

def reversestack(s,k):
q = Queue(maxsize=6)
i=1
while not s.isempty() and i<=k:
q.put(s.pop())
i=i+1
while not q.empty():
s.push(q.get())

s = stack()
s.push(11)
s.push(22)
s.push(33)
s.push(44)
s.push(55)
s.push(66)
s.display() #66 55 44 33 22 11
reversestack(s,3)
s.display() #44 55 66 33 22 11

balanced paranethsis
--------------------
() True
[] True
{} True
([]) True
([}) False
([)] False

from stack import *

def balanced(expr):
s = []
for ch in expr:
if ch=='(' or ch=='[' or ch=='{':
s.append(ch)
elif ch==')':
if s.pop()!='(':
return False
elif ch==']':
if s.pop()!='[':
return False
elif ch=='}':
if s.pop()!='{':
return False
return len(s)==0

print(balanced("()")) #True
print(balanced("([])")) #True
print(balanced("([)]")) #False
print(balanced("([{}])")) #True
print(balanced("([{]})")) #False

Representation of expressions in programming


--------------------------------------------
In general the following are the three types of represnetaion of an
expression.

1. infix expressions
2. postfix expressions
3. prefix expressions

infix -----> operand operator operand


postfix ---> operand operand operator
prefix ----> operator operand operand

infix prefix postfix


-------------------------------
a+b +ab ab+
a+b*c +abc abc*+
a*b+c*d +*ab*cd ab*cd*+

infix to postfix convertion algorithm


-------------------------------------
1. print operands in the same order they arrive.
2. if stack is empty or contains '(' on top, then push incoming operator
3. if incoming operator is ( then push it into the stack
4. if incoming operator is ) then pop items from stack until ( came
5. if the precedence of incoming symbol is >= precedence if symbol that
is existed in the top of the stack, push that symbol into the stack.
6. if the precedence of incoming symbol is < precedence of symbol existed
in the top of the stack, pop the symbol from stack and put it into
output. then compare next symbol else push that symbol into stack.
7. pop all symbols from stack put into output.

Ex:
a+b ---> ab+
a+b*c -> abc*+
(a+b)*c-> ab+c*

infix to postfix convertion implementation


------------------------------------------
def precedence(x):
if x=='(':
return 0
if x=='+' or x=='-':
return 1
if x=='*' or x=='/':
return 2
return 3

def infix_to_postfix_conversion(s):
stack = []
tokens=list(s)
result = ""
for item in tokens:
if item in "+-*/":
while len(stack)!=0 and precedence(item) <=
precedence(stack[-1]):
result = result + stack.pop()
stack.append(item)
elif item =='(':
stack.append('(')
elif item==')':
temp = None
while len(stack)!=0 and temp!='(':
temp = stack.pop()
if temp!='(':
result = result + temp
else:
result = result + item
while len(stack)!=0:
result = result + stack.pop()
return result

print(infix_to_postfix_conversion("a+b")) #ab+
print(infix_to_postfix_conversion("a+b-c*d")) #ab+cd*-
print(infix_to_postfix_conversion("a+(b-c)*d")) #abc-d*+

infix to prefix convertion algorithm


------------------------------------
1. reverse the given expression
2. replace '(' with ')' and ')' with '('
3. apply infix to postfix conversion
4. reverse the generated output

Ex:
a+b
1. b+a
2. -
3. ba+
4. +ab

Ex:a+b*c

1. c*b+a
2. -
3. cb*a+
4. +a*bc

infix to prefix convertion implementation


-----------------------------------------
def precedence(x):
if x=='(':
return 0
if x=='+' or x=='-':
return 1
if x=='*' or x=='/':
return 2
return 3

def infix_to_postfix_conversion(s):
stack = []
tokens=list(s)
result = ""
for item in tokens:
if item in "+-*/":
while len(stack)!=0 and precedence(item) <=
precedence(stack[-1]):
result = result + stack.pop()
stack.append(item)
elif item =='(':
stack.append('(')
elif item==')':
temp = None
while len(stack)!=0 and temp!='(':
temp = stack.pop()
if temp!='(':
result = result + temp
else:
result = result + item
while len(stack)!=0:
result = result + stack.pop()
return result

def replace(s):
ss=""
for i in s:
if i=='(':
ss=ss+')'
elif i==')':
ss=ss+'('
else:
ss=ss+i
return ss

def infix_to_prefix_conversion(exp):
exp = exp[::-1]
exp = replace(exp)
exp = infix_to_postfix_conversion(exp)
exp = exp[::-1]
return exp

print(infix_to_prefix_conversion("a+b")) #ab+
print(infix_to_prefix_conversion("a+b-c*d")) #+a-b*cd
print(infix_to_prefix_conversion("a+(b-c)*d")) #+a*-bcd

evalaution postfix expression algorithm


---------------------------------------
The postfix expression is used to represent albegric expression, the
expressions written in postfix form are evalauted faster when compared
with other expression.

steps:
------
1. create a stack to store operands
2. scan the expr from left to right and do the following

i. if the element is a number then push into stack


ii. if the element is an operator, pop two operands from the stack and
eval again and push result into stack.

3. when an expression is ended, the number in the stack is nothing result

evalaution postfix expression implementation


--------------------------------------------
def precedence(x):
if x=='(':
return 0
if x=='+' or x=='-':
return 1
if x=='*' or x=='/':
return 2
return 3

def infix_to_postfix_conversion(s):
stack = []
tokens=list(s)
result = ""
for item in tokens:
if item in "+-*/":
while len(stack)!=0 and precedence(item) <=
precedence(stack[-1]):
result = result + stack.pop()
stack.append(item)
elif item =='(':
stack.append('(')
elif item==')':
temp = None
while len(stack)!=0 and temp!='(':
temp = stack.pop()
if temp!='(':
result = result + temp
else:
result = result + item
while len(stack)!=0:
result = result + stack.pop()
return result

def replace(s):
ss=""
for i in s:
if i=='(':
ss=ss+')'
elif i==')':
ss=ss+'('
else:
ss=ss+i
return ss

def infix_to_prefix_conversion(exp):
exp = exp[::-1]
exp = replace(exp)
exp = infix_to_postfix_conversion(exp)
exp = exp[::-1]
return exp

def postfix_eval(exp):
stack = []
tokens = list(exp)
for token in tokens:
if token=='+':
n1 = stack.pop()
n2 = stack.pop()
stack.append(n1+n2)
elif token=='-':
n1 = stack.pop()
n2 = stack.pop()
stack.append(n1-n2)
elif token=='*':
n1 = stack.pop()
n2 = stack.pop()
stack.append(n1*n2)
elif token=='/':
n1 = stack.pop()
n2 = stack.pop()
stack.append(n1/n2)
else:
stack.append(int(token))
return stack.pop()

print(postfix_eval(infix_to_postfix_conversion("1+2"))) #3
print(postfix_eval(infix_to_postfix_conversion("1+2*3"))) #7

Queue data structure


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

Introduction:
-------------
==> First - In - First - Out (FIFO)
==> front and rear
==> front is always used to access/delete
==> rear is always used to insert

Operations on Queue:
--------------------
The following are the very common operations that can be performed on Q

1. enque or insert
2. dequeue or delete
3. getFront
4. getRear
5. isempty
6. size
7. display

Applications:
-------------
1. Single Resource and Multiple Consumers (QUEUE)
2. Operating Systems
3. Printing Queue
4. Computer Networks etc

Queue implementatoin in Python


------------------------------
1. using list
2. collections.deque
3. queue.Queue
4. implementation of queue by using list
5. implementation of queue by using linked list

1. using list
-------------
q = []

print(len(q)==0) #True
q.append(10)
q.append(20)
q.append(30)
print(q) #[10, 20, 30]
print(len(q)==0) #False
print(q[0]) #front->10
print(q[-1]) #rear -> 30
print(q.pop(0)) #10
print(q) #[20, 30]
print(q.pop(0)) #20
print(q) #[30]

2. collections.deque
--------------------
from collections import deque
q = deque()

print(len(q)==0) #True
q.append(111)
q.append(222)
q.append(333)
print(len(q)==0) #False
print(q) #[111, 222, 333]
print(q[0]) #111
print(q[-1]) #333
print(q.popleft()) #111
print(q) #[222, 333]

3. queue.Queue
--------------
from queue import Queue
q = Queue()
print(q.qsize()) #0
print(q.empty()) #True
q.put(11)
q.put(22)
q.put(33)
q.put(44)
print(q.qsize()) #4
print(q.empty()) #False
print(q)
print(q.get()) #11
print(q.get()) #11

4. implementation of queue by using list


----------------------------------------
class queue:

def __init__(self,cap):
self.q = [None]*cap
self.cap = cap
self.count = 0

def isfull(self):
return self.count==self.cap

def isempty(self):
return self.count==0

def size(self):
return self.count

def display(self):
if self.isempty():
print("q is empty")
return
for i in range(self.count):
print(self.q[i],end=" ")
print()

def delete(self):
if self.isempty():
print("q is empty")
return
res = self.q[0]
for i in range(self.count-1):
self.q[i] = self.q[i+1]
self.count = self.count-1
return res

def insert(self,data):
if self.isfull():
print("q is full")
return
self.q[self.count] = data
self.count = self.count + 1

q = queue(5)
print(q.size()) #0
print(q.isempty()) #True
print(q.isfull()) #False

q.insert(10)
q.insert(20)
q.insert(30)
q.insert(40)
q.display() #10 20 30 40
q.insert(50)
q.insert(60)
q.display() #10 20 30 40 50
print(q.delete()) #10
q.display() #20 30 40 50
q.insert(60)
print(q.size()) #4
print(q.isempty()) #False
print(q.isfull()) #True

C:\test>py test.py
0
True
False
10 20 30 40
q is full
10 20 30 40 50
10
20 30 40 50
5
False
True

5. implementation of queue by using linked list


-----------------------------------------------
linked list ---> insert at last and delete at first

class MyQueue:

class Node:

def __init__(self,data):
self.data = data
self.next = None

def __init__(self):
self.front = None
self.rear = None
self.count = 0

def size(self):
return self.count

def isempty(self):
return self.count==0

def getfront(self):
return self.front.data

def getrear(self):
return self.rear.data

def display(self):
if self.isempty():
print("Q is empty")
return
curr = self.front
while curr!=None:
print(curr.data,end=" ")
curr = curr.next
print()

def insert(self,data):
newnode = self.Node(data)
if self.rear==None:
self.front = newnode
else:
self.rear.next = newnode
self.rear = newnode
self.count = self.count + 1

def delete(self):
if self.isempty():
print("Q is empty")
return
res = self.front.data
self.front = self.front.next
if self.front==None:
self.rear = None
self.count = self.count - 1
return res

q = MyQueue()
print(q.isempty()) #True
print(q.size()) #0

q.insert(10)
q.insert(20)
q.insert(30)

q.display() #10 20 30

print(q.isempty()) #False
print(q.size()) #3

print(q.delete())

q.display() #20 30

print(q.isempty()) #False
print(q.size()) #2

C:\test>py test.py
True
0
10 20 30
False
3
10
20 30
False
2

Hashtable Data Structure


------------------------
Introduction:
-------------
In the case of searching algorithm, consider a problem we searching for
an object in 100 objects, if the objects are not in sorted order then we
will search for the object continues one by one this method is called as
linear search which takes O(n).

If the objects are arranged in sorting order, then we can search for
given object by using binary search method by reducing the number of
comparsions, in this case the complexity of this alg is O(logN).

It is possible to get the location of given key by using some magic


methods. almost its time complexity is O(1) i.e. that is constant time .

hashtable:
----------
==> hashtable is a data structure that maps keys to values.
==> each position of the table is called as a slot or bucket or record.
==> hashtable uses hash function to calculate the position of object.

The process of storing data using hash function in the hashtable is as


follows.

1. create an array of size 'n' this list is called as hashtable


2. find hash code of the object by using hash function.
3. take modulo division of the given object with table size hash fun
4. finally store this object in the position returned by hash fun.

Operations:
-----------
1. insertion
2. deletion
3. display
4. search

Implementation of hashtable
---------------------------
class Hashtable:

def __init__(self,size):
self.size = size
self.L = [-1]*size

def print(self):
print("Hashtable Content: ")
for i in range(self.size):
print(f"{i} ====> {self.L[i]}")
def compute(self,value):
return value%self.size

def add(self,value):
hcode = self.compute(value)
if self.L[hcode]==-1:
self.L[hcode] = value
return True
return False

def delete(self,value):
hcode = self.compute(value)
if self.L[hcode]!=-1 and self.L[hcode]==value:
self.L[hcode] = -1
return True
return False

def search(self,value):
hcode = self.compute(value)
if self.L[hcode]==value:
return True
return False

h = Hashtable(10)
print(h.add(13))
print(h.add(55))
print(h.add(99))
print(h.add(78))
print(h.add(53)) #False
h.print()
print(h.delete(56)) #False
print(h.delete(55)) #True
h.print()
print(h.search(13)) #True
print(h.search(55)) #False

C:\test>py test.py
True
True
True
True
False
Hashtable Content:
0 ====> -1
1 ====> -1
2 ====> -1
3 ====> 13
4 ====> -1
5 ====> 55
6 ====> -1
7 ====> -1
8 ====> 78
9 ====> 99
False
True
Hashtable Content:
0 ====> -1
1 ====> -1
2 ====> -1
3 ====> 13
4 ====> -1
5 ====> -1
6 ====> -1
7 ====> -1
8 ====> 78
9 ====> 99
True
False

C:\test>

Collision:
----------
When a function generates the same index/hash code for two or more
objects, then it is very difficult to store that object into the
hashtable in such cases collision will occur. We have to write a hash
function effificently without collision. practicaally it is impossible.
we have collision resolution methods

1. linear probing
2. quadratic probing
3. seperate chaining

1. linear probing
-----------------
Insert 3, 13, 23, 33, 43, 53, 63 etc

class Hashtable:

def __init__(self,size):
self.size = size
self.L = [-1]*size

def print(self):
print("Hashtable Content: ")
for i in range(self.size):
print(f"{i} ====> {self.L[i]}")

def compute(self,value):
return value%self.size

def compute1(self,index): #linear


return index

def add(self,value):
hcode = self.compute(value)
for i in range(self.size):
if self.L[hcode]==-1:
self.L[hcode] = value
return True
hcode = hcode + self.compute1(i)
hcode = hcode % self.size
return False

def delete(self,value):
hcode = self.compute(value)
for i in range(self.size):
if self.L[hcode]==-1 and self.L[hcode]==value:
self.L[hcode] = -1
return True
hcode = hcode + self.compute1(i)
hcode = hcode % self.size
return False

def search(self,value):
hcode = self.compute(value)
for i in range(self.size):
if self.L[hcode]==value:
return True
hcode = hcode + self.compute1(i)
hcode = hcode % self.size
return False

h = Hashtable(10)
print(h.add(3))
print(h.add(13))
print(h.add(23))
print(h.add(33))
print(h.add(43))
print(h.add(53))
print(h.add(63))
h.print()

C:\test>py test.py
True
True
True
True
True
True
False
Hashtable Content:
0 ====> -1
1 ====> 53
2 ====> -1
3 ====> 3
4 ====> 13
5 ====> -1
6 ====> 23
7 ====> -1
8 ====> 43
9 ====> 33

2. quadratic probing
--------------------
class Hashtable:

def __init__(self,size):
self.size = size
self.L = [-1]*size

def print(self):
print("Hashtable Content: ")
for i in range(self.size):
print(f"{i} ====> {self.L[i]}")

def compute(self,value):
return value%self.size

def compute1(self,index): #linear


return index

def compute2(self,index): #quadratic


return index*index

def add(self,value):
hcode = self.compute(value)
for i in range(self.size):
if self.L[hcode]==-1:
self.L[hcode] = value
return True
hcode = hcode + self.compute2(i)
hcode = hcode % self.size
return False

def delete(self,value):
hcode = self.compute(value)
for i in range(self.size):
if self.L[hcode]==-1 and self.L[hcode]==value:
self.L[hcode] = -1
return True
hcode = hcode + self.compute2(i)
hcode = hcode % self.size
return False

def search(self,value):
hcode = self.compute(value)
for i in range(self.size):
if self.L[hcode]==value:
return True
hcode = hcode + self.compute2(i)
hcode = hcode % self.size
return False
h = Hashtable(10)
print(h.add(5))
print(h.add(15))
print(h.add(25))
print(h.add(35))
print(h.add(45))
h.print()

C:\test>py test.py
True
True
True
True
False
Hashtable Content:
0 ====> 25
1 ====> -1
2 ====> -1
3 ====> -1
4 ====> -1
5 ====> 5
6 ====> 15
7 ====> -1
8 ====> -1
9 ====> 35

3. seperate chaining
--------------------
class Hashtable:

class Node:

def __init__(self,value,next=None):
self.value = value
self.next = next

def __init__(self,size):
self.size = size
self.L = [None]*size

def print(self):
print("Hashtable Content: ")
for i in range(self.size):
print(f"{i} ====> ",end=" ")
temp = self.L[i]
while temp!=None:
print(temp.value,end=" => ")
temp = temp.next
print("None")

def compute(self,value):
return value%self.size
def add(self,value):
hcode = self.compute(value)
self.L[hcode] = self.Node(value,self.L[hcode])

def delete(self,value):
hcode = self.compute(value)
temp = self.L[hcode]
if temp!=None and temp.value == value:
self.L[hcode] = temp.next
return True
while temp!=None:
temp1 = temp.next
if temp1!=None and temp1.value == value:
temp.next = temp1.next
return True
temp = temp1
return False

def search(self,value):
hcode = self.compute(value)
temp = self.L[hcode]
while temp!=None:
if temp.value == value:
return True
temp = temp.next
return False

h = Hashtable(10)
h.add(3)
h.add(13)
h.add(23)
h.add(34)
h.add(77)
h.add(29)
h.add(22)
h.add(33)
h.add(43)
h.add(53)
h.print()
print(h.delete(23))
h.print()
print(h.search(13))
print(h.search(23))

C:\test>py test.py
Hashtable Content:
0 ====> None
1 ====> None
2 ====> 22 => None
3 ====> 53 => 43 => 33 => 23 => 13 => 3 => None
4 ====> 34 => None
5 ====> None
6 ====> None
7 ====> 77 => None
8 ====> None
9 ====> 29 => None
True
Hashtable Content:
0 ====> None
1 ====> None
2 ====> 22 => None
3 ====> 53 => 43 => 33 => 13 => 3 => None
4 ====> 34 => None
5 ====> None
6 ====> None
7 ====> 77 => None
8 ====> None
9 ====> 29 => None
True
False

Bit Manipulations:
------------------
Introduction:
-------------
It is used to perform operations on binary numbers, because the decimal
numbers are internally converted into binary form for eval purpose, that
why if you are perform any operations directly on binary numbers
efficiency will be imporved for that we should go for bit manipulations.

Number Systems:
---------------
A digital system can understand the digits by using the following
components

1. digit
2. position of that digit
3. base of that number system

types of number systems:


------------------------
The main purpose of number systesm are used to represent the numbers in
digital system.

1. Binary number system -------> base:2 ---> 0, 1


2. Decimal number system ------> base:10 --> 0,1,2,3,4,5,6,7,8,9
3. Octal number system --------> base:8 ---> 0,1,2,3,4,5,6,7
4. Hexadecimal number system --> base:16 -->
0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f

Decimal Binary Octal Hexadecimal


-----------------------------------------------
0 0000 0 0
1 0001 1 1
2 0010 2 2
3 0011 3 3
4 0100 4 4
5 0101 5 5
6 0110 6 6
7 0111 7 7
8 1000 10 8
9 1001 11 9
10 1010 12 a
11 1011 13 b
12 1100 14 c
13 1101 15 d
14 1110 16 e
15 1111 17 f

Decimal to Binary Conversion:


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Method1:
--------
1. Divide the number by 2, where it gives Quotient and Remainder.
2. We have to store remainders in a container.
3. Print the stored values in reverse order.

Ex:
n = 13
Quotient Remainder
13/2 6 1
6/2 3 0
3/2 1 1
1/2 0 1

Ans: 1101

Method2:
--------
Find binary equivalent for the given number is 8-4-2-1

Ex:
8 4 2 1
n=13 ---> 1 1 0 1
n=12 ---> 1 1 0 0
n=5 ----> 0 1 0 1

Binary to Decimal Conversion:


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method1:
--------
1. Read symbols from right to left.
2. multiply each bit with 2 power x where x = 0,1,2,3, etc
3. sum of these values nothing but our result

Ex: 1101

1x2^0 = 1x1 = 1
0x2^1 = 0x2 = 0
1x2^2 = 1x4 = 4
1x2^3 = 1x8 = 8
---------------
13

Method2:
--------
By using 8 4 2 1 code we can find decimal number for the given binary

1010 ----> 8+2 = 10


1101 ----> 8+4+0+1 = 13

Bitwise operators:
------------------
==> it is a special operators that are existed in all most all PLs
==> it is an efficient way to work with apps
==> it is very fast when compared with other operators
==> it requires linear time for execution
==> easy implement

1. bitwise and ------> &


2. bitwise or -------> |
3. bitwise x-or -----> ^
4. left shift -------> <<
5. right shift ------> >>
6. complement -------> ~

val1 val2 val1 & val2 val1 | val2 val1 ^ val2


------------------------------------------------------------
0 0 0 0 0

0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

left shift -----> a<<b = a*2^b


right shift ----> a>>b = a/2^b

complement -----> ~n = -(n+1)

Advantages of Bitwise operators


-------------------------------
1. speed
2. space optimization
3. bit manipulations
4. code simplifications
5. readability will be improved
6. data encryption etc

Applications
------------
1. Even or Odd number
2. Swapping of two numbers
3. Lower case to Upper case conversion
4. Upper case to Lower case conversion
1. Even or Odd number
---------------------
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110

if LSB is 0 then that number is EVEN


if LSB is 1 then that number is ODD

n & 1 == 0 then result is EVEN else ODD

n=int(input())
if (n&1)==0:
print("even")
else:
print("odd")

C:\test>py test.py
4
even

C:\test>py test.py
5
odd

2. Swapping of two numbers


--------------------------
a=int(input())
b=int(input())
print(f"before: a={a} and b={b}")
a = a ^ b
b = a ^ b
a = a ^ b
print(f"after : a={a} and b={b}")

C:\test>py test.py
10
30
before: a=10 and b=30
after : a=30 and b=10

3. Lower case to Upper case conversion


--------------------------------------
name = input("Enter any name in lower case:")
l = []
for ch in name:
l.append(chr(ord(ch)^32))
print(''.join(l))

C:\test>py test.py
Enter any name in lower case:prakash
PRAKASH

4. Upper case to Lower case conversion


--------------------------------------
name = input("Enter any name in upper case:")
l = []
for ch in name:
l.append(chr(ord(ch)|32))
print(''.join(l))

C:\test>py test.py
Enter any name in lower case:prakash
PRAKASH

C:\test>py test.py
Enter any name in upper case:JAVA
java

Dynamic Programming:
--------------------
==> It is same as recursion.
==> Recursion with optimized solution is nothing but DP.

Ex:
---
Fib number program by using recursion.

def fib(n):
global c
c=c+1
if n==0 or n==1:
return n
return fib(n-1)+fib(n-2)

c = 0
n=int(input("Enter nth term: ")) #5
# 0 1 1 2 3 5 8 13 . . .
print(fib(n)) #5
print(c)

C:\test>py test.py
Enter nth term: 5
5
15

We can implement DP by using the following two methods...

1. Memoization (recursion)
2. Tabulation (Arrays/List)

Ex:
---
def fib(n,L):
global c
c=c+1
if n==0 or n==1:
return n
if L[n]!=0:
return L[n]
L[n] = fib(n-1,L) + fib(n-2,L)
return L[n]

c = 0
n=int(input("Enter nth term: ")) #5
# 0 1 1 2 3 5 8 13 . . .
L = [0]*(n+1)
print(fib(n,L)) #5
print(c)

C:\test>py test.py
Enter nth term: 5
5
9

Ex:
---
def fib(n):
L = [0]*(n+1)
L[0] = 0
L[1] = 1
for i in range(2,n+1):
L[i] = L[i-1] + L[i-2]
return L[n]

n=int(input("Enter nth term: ")) #5


# 0 1 1 2 3 5 8 13 . . .
print(fib(n)) #5

C:\test>py test.py
Enter nth term: 5
5

22. priority queues / heaps


---------------------------
It is nothingbut a tree, heap tree

heap tree ----> where min/max element will be there as root node.

Ex:
---
from queue import PriorityQueue

q = PriorityQueue()

q.put(4)
q.put(2)
q.put(5)
q.put(1)
q.put(3)

while not q.empty():


print(q.get())

C:\test>py test.py
1
2
3
4
5

Ex:
---
from queue import PriorityQueue

q = PriorityQueue()

q.put((4,'Read'))
q.put((2,'Play'))
q.put((5,'Write'))
q.put((1,'Code'))
q.put((3,'Study'))

while not q.empty():


print(q.get())

C:\test>py test.py
(1, 'Code')
(2, 'Play')
(3, 'Study')
(4, 'Read')
(5, 'Write')

16. greedy algorithms


~~~~~~~~~~~~~~~~~~~~~
The main purpose of greedy algs are to find optimal solutions

Coins
-----
100-48 = 52

Return 52/-

coins ---> [50, 10, 5, 2, 1]

1 ---> 50x1 + 2x1 = 50+2 = 52 ----> 2


2 ---> 10x5 + 2x1 = 50+2 = 52 ----> 6
3 ---> 5x10 + 2x1 = 50+2 = 52 ----> 11
etc

result = 0

while(items)
{
i = select_item
if item is feasible
then add that item to result

return result

Ex:
---
def fun(coins,amount):
result = 0
coins.sort(reverse=True)
for coin in coins:
if coin<=amount:
x = amount//coin
result = result+x
amount = amount - x*coin
if amount==0:
break
return result

coins = [5, 10, 2, 1, 50]


amount = 52
print(fun(coins,amount))

C:\test>py test.py
2

You might also like