Unit 5

Download as pdf or txt
Download as pdf or txt
You are on page 1of 33

Recursion and Iteration

• Recursion is defined as a process in which a function calls itself repeatedly.


Recursion uses a selection structure. If the recursion step does not reduce the
problem in a manner that converges on some condition, called base condition,
then an infinite recursion occurs. An infinite recursion can crash the system.
Recursion terminates when a base case is recognized.
• Iteration is defined as the repetition of computational or mathematical procedures
that continues until the controlling condition becomes false. It uses a
repetition structure. If the loop condition test never becomes false, then an infinite
loop occurs with iteration. This infinite looping uses CPU cycles repeatedly. An
iteration terminates when the loop condition fails. Iteration consumes less memory
but makes the code longer which is difficult to read and write.
Difference between Recursion and Iteration
Recursion Iteration
Recursion uses the selection structure. Iteration uses the repetition structure.

Infinite recursion occurs if the step in recursion An infinite loop occurs when the condition in the
doesn't reduce the problem to a smaller problem. It loop doesn't become False ever.
also becomes infinite recursion if it doesn't convert
on a specific condition. This specific condition is
known as the base case.

The system crashes when infinite recursion is Iteration uses the CPU cycles again and again when
encountered. an infinite loop occurs.

Recursion terminates when the base case is met. Iteration terminates when the condition in the loop
fails.
Recursion is slower than iteration since it has the Iteration is quick in comparison to recursion. It
overhead of maintaining and updating the stack. doesn't utilize the stack.

Recursion uses more memory in comparison to Iteration uses less memory in comparison to
iteration. recursion.
Recursion reduces the size of the code. Iteration increases the size of the code.
Recursive Fibonacci
A Fibonacci sequence is the integer sequence of 0, 1, 1, 2, 3, 5, 8....
The first two terms are 0 and 1. All other terms are obtained by adding the preceding
two terms.This means to say the nth term is the sum of (n-1)th and (n-2)th term.
# Python program to display the Fibonacci sequence

def recur_fibo(n):
if n <= 1:
return n
else:
return(recur_fibo(n-1) + recur_fibo(n-2))

nterms = 10

# check if the number of terms is valid


if nterms <= 0:
print("Plese enter a positive integer")
else:
print("Fibonacci sequence:")
for i in range(nterms):
print(recur_fibo(i))
Tower of Hanoi
Tower of Hanoi is a mathematical puzzle where we have three rods and n disks. The
objective of the puzzle is to move the entire stack to another rod, obeying the
following simple rules:
1) Only one disk can be moved at a time.
2) Each move consists of taking the upper disk from one of the stacks and placing it on
top of another stack i.e. a disk can only be moved if it is the uppermost disk on a
stack.
3) No disk may be placed on top of a smaller disk.
Note: Transferring the top n-1 disks from the source rod to the Auxiliary rod can again
be thought of as a fresh problem and can be solved in the same manner.
Tower of Hanoi
Tower of Hanoi

def TowerOfHanoi(n , source, destination, auxiliary):


if n==1:
print ("Move disk 1 from source",source,"to destination",destination)
return
TowerOfHanoi(n-1, source, auxiliary, destination)
print ("Move disk",n,"from source",source,"to destination",destination)
TowerOfHanoi(n-1, auxiliary, destination, source)

# Driver code
n=4
TowerOfHanoi(n,'A','B','C')
# A, C, B are the name of rods
Searching algorithm
What is Searching Algorithm?
Searching Algorithms are designed to check for an element or retrieve an element from any data
structure where it is stored.

Based on the type of search operation, these algorithms are generally classified into two categories:
1. Sequential Search: In this, the list or array is traversed sequentially and every element is checked.
For example Linear Search.
Linear Search to find the element “20” in a given list of numbers
Code for liner search
def search(a, l, x):

# Traversing the array


for i in range(l):
if (a[i] == x):
return i
return -1

print("The given array is ", a)


x = 45
l=len(a)
a = [10, 8, 6, 4, 2,45]
print("Element to be searched is ", x)
search(a, l, x)
Time complexity
Time Complexity Analysis
The Best Case: occurs when the target element is the first element of the array. The number
of comparisons, in this case, is 1. So, the time complexity is O(1).

The Average Case: The target element will be somewhere in the middle of the array. The
number of comparisons, in this case, will be N/2. So, the time complexity will be O(N) (the
constant being ignored).

The Worst Case occurs when the target element is the last element in the array or not in the
array. In this case, we have to traverse the entire array, so the number of comparisons will be
N. So, the time complexity will be O(N).
Binary search
2. Interval Search: These algorithms are specifically designed to search sorted data
structures. These types of searching algorithms are much more efficient than Linear Search
as they repeatedly target the center of the search structure and divide the search space in
half. For Example Binary Search.
Binary Search to find the element “23” in a given list of numbers
Time complexity
Time Complexity Analysis
The Best Case occurs when the target element is the middle element of the array. The
number of comparisons, in this case, is 1. So, the time complexity is O(1).

The Average Case: The target element will be somewhere in the array. So, the time
complexity will be O(logN).

The Worst Case occurs when the target element is not in the list or it is away from the
middle element. So, the time complexity will be O(logN).
Code for binary search
def search(nums, target):
start = 0
end = len(nums)-1
while start <= end:
mid = start + (end-start)//2
if nums[mid] > target:
end = mid-1
elif nums[mid] < target:
start = mid+1
else:
return mid
return -1
if __name__ == '__main__’:
nums = [2, 12, 15, 17, 27, 29, 45]
target = 17
print(search(nums, target))
Merge Sort
Merge Sort is one of the most popular sorting algorithms that is based
on the principle of Divide and Conquer Algorithm.
Here, a problem is divided into multiple sub-problems. Each sub-
problem is solved individually. Finally, sub-problems are combined to
form the final solution.
Algorithm
def merge_sort(array, left_index, right_index):
if left_index >= right_index:
return middle = (left_index + right_index)//2
merge_sort(array, left_index, middle)
merge_sort(array, middle + 1, right_index)
merge(array, left_index, right_index, middle)
def mergeSort(arr):
if len(arr) > 1:

# Create sub_array2 ← A[start..mid] and sub_array2 ← A[mid+1..end]


mid = len(arr)//2
sub_array1 = arr[:mid]
sub_array2 = arr[mid:]

# Sort the two halves


mergeSort(sub_array1)
mergeSort(sub_array2)

# Initial values for pointers that we use to keep track of where we are in each array
i=j=k=0

# Until we reach the end of either start or end, pick larger among
# elements start and end and place them in the correct position in the sorted array
while i < len(sub_array1) and j < len(sub_array2):
if sub_array1[i] < sub_array2[j]:
arr[k] = sub_array1[i]
i += 1
else:
arr[k] = sub_array2[j]
j += 1
k += 1

# When all elements are traversed in either arr1 or arr2,


# pick up the remaining elements and put in sorted array
while i < len(sub_array1):
arr[k] = sub_array1[i]
i += 1
k += 1

while j < len(sub_array2):


arr[k] = sub_array2[j]
j += 1
k += 1

arr = [10, 9, 2, 4, 6, 13]


Selection Sort
The selection sort algorithm sorts an array by repeatedly finding the minimum element
(considering ascending order) from the unsorted part and putting it at the beginning.
The algorithm maintains two subarrays in a given array.
1) The subarray which is already sorted.
2) Remaining subarray which is unsorted. In every iteration of the selection sort, the
minimum element (considering ascending order) from the unsorted subarray is picked
and moved to the sorted subarray.
Algorithm
1.selection_sort(array)
2. repeat (0, length - 1) times
3. set the first unsorted element as the minimum
4. for each of the unsorted elements
5. if element < currentMinimum
6. set element as new minimum
7. swap minimum with first unsorted position
8.end selection_sort
# Selection sort in Python
# time complexity O(n*n)
#sorting by finding min_index
def selectionSort(array, size):

for ind in range(size):


min_index = ind

for j in range(ind + 1, size):


# select the minimum element in every iteration
if array[j] < array[min_index]:
min_index = j
# swapping the elements to sort the array
(array[ind], array[min_index]) = (array[min_index], array[ind])

arr = [-2, 45, 0, 11, -9,88,-97,-202,747]


size = len(arr)
selectionSort(arr, size)
print('The array after sorting in Ascending Order by selection sort is:')
print(arr)

Time Complexity: O(n2).


Auxiliary Space: O(1).
Merge List
A list is a data structure in Python that contains a sequence of elements. We can have
many lists in our code, which sometimes we may need to merge, join, or concatenate.

• merge two lists in Python through append()


• merge two lists in Python using the + operator
• merge two lists in Python using the List comprehension
• extend() method
1. Using append() function
• The append() method in Python adds a single item to the existing list.
• It doesn't return a new list of items. Instead, it modifies the original list by adding
the item to the end of the list.
Append method
# python program to demonstrate merging
# of lists using python
def append_lists():
#list 1
ls1 = [1,2,3,4]
#list 2
ls2 = [5,6,7,8]

#iterating over list2


for i in ls2:
#appending in list1
ls1.append(i)
print(ls1)

#function call
append_lists()
‘+’ Operator
2. Using the '+' Operator

The '+' operator is a multipurpose operator, which we can use for arithmetic
calculations and for merging purposes, strings, lists, etc.

#merge lists Using the '+' operator


def merge():
#list 1
ls1 = [15,20,35,40]
#list 2
ls2 = [99,44,13]
#merging using '+' operator
ls = ls1 + ls2
print(ls)

#function call
merge()
List comprehension
3. Using List Comprehension
• List comprehension offers a shorter and crisper syntax when we want to create a
new list based on the values of an existing list.
• It may also be called an alternative to the loop method.

def list_comprehension():
num1=[1,2,3]
num2=[4,5,6]
#using list comprehension
num3=[x for n in (num1,num2) for x in n]
print(num3)
#function call
list_comprehension()
Extend Method
4. Using the extend() method
• The extend() method adds all the elements of an iterable (list, tuple, string, etc.) to
the end of the list.
• It updates the original list itself. Hence its return type is None.
• It can be used to merge two lists in Python.

def extend_lists():
#list 1
ls1 = [11,19,25,40]
#list 2
ls2 = [31,84,13]
ls1.extend(ls2)
print(ls1)
#function call
extend_lists()
High Order Sort(Function)
A function in Python with another function as an argument or returns a
function as output is called the High order function. Let’s see the
properties −
• A function is an instance of the Object type.
• HOF can store the function in a variable.
• HOF can pass the function as a parameter to another function.
• HOF can return the function from a function.
• HOF can store them in data structures such as hash tables, lists, …
Functions as objects
# Python program to illustrate functions
# can be treated as objects
def shout(text):
return text.upper()

print(shout('Hello'))

# Assigning function to a variable


yell = shout

print(yell('Hello'))
Passing Function as an argument to other function
# Python program to illustrate functions
# can be passed as arguments to other functions
def shout(text):
return text.upper()

def whisper(text):
return text.lower()

def greet(func):
# storing the function in a variable
greeting = func("Hi, I am created by a function \
passed as an argument.")
print(greeting)

greet(shout)
greet(whisper)
Output:
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT. hi, i am
created by a function passed as an argument.
Returning function
As functions are objects, we can also return a function from another
function.
# Python program to illustrate functions
# Functions can return another function

def create_adder(x):
def adder(y):
return x + y

return adder

add_15 = create_adder(15)

print(add_15(10))
Output:
25
Decorators
• Decorators are the most common use of higher-order functions in Python.
• It allows programmers to modify the behavior of a function or class.
• Decorators allow us to wrap another function in order to extend the behavior of the
wrapped function, without permanently modifying it.
• In Decorators, functions are taken as the argument into another function and then
called inside the wrapper function.

Syntax:
@gfg_decorator def hello_decorator(): . . .
Example
# defining a decorator
def hello_decorator(func):

# inner1 is a Wrapper function in


# which the argument is called

# inner function can access the outer local


# functions like in this case "func"
def inner1():
print("Hello, this is before function execution")

# calling the actual function now


# inside the wrapper function.
func()

print("This is after function execution")

return inner1

# defining a function, to be called inside wrapper


def function_to_be_used():
print("This is inside the function !!")

# passing 'function_to_be_used' inside the


# decorator to control its behavior
function_to_be_used = hello_decorator(function_to_be_used)

# calling the function


function_to_be_used()
Built-in Functions
We have some built-in higher-order functions in Python.
1. Map Function
Syntax:- map (function, sequence)
It takes a function and sequence as the arguments and then returns a list after applying
the function to each sequence’s items.

#function for performing the multiplication operation


def multiply(n):
#retuning the values
return n*n
#driver code to test the program
num = (10,15,78,25)
#map function(function, sequence/iterable)
result = map(multiply, num)
#returning the result in a list
print(list(result))
Output : [100, 225, 6084, 625]
Filter function
2. Filter function
Syntax:- filter (function, sequence/iterable)
The filter takes two parameters, the first one is a function and the second one is a
sequence.
Then it returns an iterator that passed through a function test for those elements
function returns True.
Let us consider an example, suppose we want to filter out those numbers which are
greater than 50 from a given list of numbers.
#driver code to test the program
numbers = [55, 92, 27, 48, 34, 62, 71, 18, 28, 43]
#function to check the numbers whether they are greater than 50 or not
def numCheck(n):
if n < 50:
return False
else:
return True
#passing through the filter function
result = filter(numCheck, numbers)
#displaying the result
for n in result:
print(n)
Output : 55 92 62 71
Sorted Function
3. Sorted Function
The sorted function simply sorts a given sequence in a specific
order. (either in ascending order or in descending order)
Syntax:- sorted (sequence, key (optional), reverse(optional))
Note:- The difference is that the sort() function doesn’t return any
value but in the case of the sorted() function, it returns an iterable
list.
#declaring string
str = "codespeedy“
#calling sorted function
result = sorted(str) # sorting string
#displaying result
print(result)
Reduce Function
4. Reduce Function
Syntax:- reduce (function, sequence/iterable)
It takes a sequence/iterable of input data then applies it to the
function provided and gives a single result.

#import the reduce function from functools module


from functools import reduce
#function to divide two numbers
def addition(a,b):
return a+b
#taking input
n=map(int, input("Enter the numbers you want to add: ").split())
#reduce function
reduce(addition,n)

You might also like