Recursion is a programming method where a function calls itself to tackle smaller parts of a bigger problem. In Python, it is often used for tasks that can be divided into similar, smaller problems. Here are some typical examples of recursion in Python.

1. Factorial Calculation

A classic example of recursion is calculating the factorial of a number.

def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)

# Example usage
print(factorial(5)) # Output: 120

2. Fibonacci Sequence

The Fibonacci sequence is another well-known problem solved using recursion.

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

# Example usage
print(fibonacci(6)) # Output: 8

3. Sum of a List

Recursion can be used to calculate the sum of elements in a list.

def recursive_sum(lst):
if not lst: # Base case: if the list is empty
return 0
else:
return lst[0] + recursive_sum(lst[1:])

# Example usage
print(recursive_sum([1, 2, 3, 4, 5])) # Output: 15

4. Traversing Nested Data Structures (e.g., Nested Lists or Dictionaries)

Recursion helps traverse or flatten nested data structures like lists or dictionaries.

Traversing Nested Lists:

def traverse_list(lst):
for element in lst:
if isinstance(element, list):
traverse_list(element)
else:
print(element)

# Example usage
nested_list = [1, [2, 3, [4, 5]], 6]
traverse_list(nested_list) # Output: 1 2 3 4 5 6

Traversing Nested Dictionaries:

def traverse_dict(d):
for key, value in d.items():
if isinstance(value, dict):
traverse_dict(value)
else:
print(f"{key}: {value}")

# Example usage
nested_dict = {
'a': 1,
'b': {'x': 10, 'y': {'z': 20}},
'c': 3
}
traverse_dict(nested_dict)
# Output:
# a: 1
# x: 10
# z: 20
# c: 3

5. Binary Search

Recursion can be used to implement binary search, an efficient algorithm for searching sorted lists.

def binary_search(arr, target, low, high):
if low > high:
return -1 # Target not found
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
return binary_search(arr, target, mid + 1, high)
else:
return binary_search(arr, target, low, mid - 1)

# Example usage
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(binary_search(arr, 5, 0, len(arr) - 1)) # Output: 4

6. Tower of Hanoi Problem

This is a classic recursion problem. The goal is to move a set of disks from one rod to another while following certain rules.

def hanoi(n, source, destination, auxiliary):
if n == 1:
print(f"Move disk 1 from {source} to {destination}")
else:
hanoi(n - 1, source, auxiliary, destination)
print(f"Move disk {n} from {source} to {destination}")
hanoi(n - 1, auxiliary, destination, source)

# Example usage
hanoi(3, 'A', 'C', 'B')
# Output:
# Move disk 1 from A to C
# Move disk 2 from A to B
# Move disk 1 from C to B
# Move disk 3 from A to C
# Move disk 1 from B to A
# Move disk 2 from B to C
# Move disk 1 from A to C

7. Permutations of a String or List

Recursion is often used to generate all permutations of a string or list.

def permutations(s):
if len(s) == 1:
return [s]
else:
result = []
for i in range(len(s)):
for p in permutations(s[:i] + s[i+1:]):
result.append(s[i] + p)
return result

# Example usage
print(permutations("abc")) # Output: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

8. Solving Maze Problems

Recursion is used to explore possible paths in a maze. This approach can be adapted to a wide variety of problems that involve pathfinding.

def solve_maze(maze, x, y, solution):
# Base case: reached the destination
if x == len(maze) - 1 and y == len(maze[0]) - 1 and maze[x][y] == 1:
solution[x][y] = 1
return True

# Check if maze[x][y] is valid
if 0 <= x < len(maze) and 0 <= y < len(maze[0]) and maze[x][y] == 1:
solution[x][y] = 1

# Move right
if solve_maze(maze, x, y + 1, solution):
return True

# Move down
if solve_maze(maze, x + 1, y, solution):
return True

# If none of the above worked, backtrack
solution[x][y] = 0
return False

return False

# Example usage
maze = [
[1, 0, 0, 0],
[1, 1, 0, 1],
[0, 1, 0, 0],
[1, 1, 1, 1]
]
solution = [[0 for _ in range(len(maze[0]))] for _ in range(len(maze))]
solve_maze(maze, 0, 0, solution)

for row in solution:
print(row)
# Output (solution path):
# [1, 0, 0, 0]
# [1, 1, 0, 0]
# [0, 1, 0, 0]
# [0, 1, 1, 1]

9. Tree Traversals

Recursion is widely used in tree data structures for traversing trees (in-order, pre-order, post-order).

class Node:
def __init__(self, value):
self.left = None
self.right = None
self.value = value

def in_order_traversal(root):
if root:
in_order_traversal(root.left)
print(root.value, end=" ")
in_order_traversal(root.right)

# Example usage
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)

in_order_traversal(root) # Output: 4 2 5 1 3

10. Divide and Conquer Algorithms

Recursion is fundamental in divide and conquer algorithms, like merge sort or quicksort.

Merge Sort:

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        left_half = arr[:mid]
        right_half = arr[mid:]

        merge_sort(left_half)
        merge_sort(right_half)

        i = j = k = 0

        while i < len(left_half) and j < len(right_half):
            if left_half[i] < right_half[j]:
                arr[k] = left_half[i]
                i += 1
            else:
                arr[k] = right_half[j]
                j += 1
            k += 1

        while i < len(left_half):
            arr[k] = left_half[i]
            i += 1
            k += 1

        while j < len(right_half):
            arr[k] = right_half[j]
            j += 1
            k += 1

# Example usage
arr = [38, 27, 43, 3, 9, 82, 10]
merge_sort(arr)
print(arr)  # Output: [3, 9, 10, 27, 38, 43, 82]

Also, read: Top Books Data Engineering Code Pipelines

Key Takeaways:

  • Breaking down problems: Recursion is useful for issues that can be divided into smaller subproblems.
  • Base case: Every recursive function should have a base case to prevent infinite recursion.
  • Efficiency: Recursion can make some problems easier to conceptualize. However, it is not always the most efficient solution. This is due to potential overhead in function calls and space complexity.
Fediverse reactions

Discover more from Srinimf

Subscribe now to keep reading and get access to the full archive.

Continue reading