DEV Community

Nitinn S Kulkarni
Nitinn S Kulkarni

Posted on

Part 5: Recursion and Backtracking โ€“ Solving Complex Problems Elegantly in Python

๐Ÿš€ Introduction

Recursion and backtracking are two core techniques that unlock powerful problem-solving patterns โ€” especially when dealing with trees, permutations, combinations, puzzles, and pathfinding.

In this post, weโ€™ll explore:

โœ… What recursion is and how it works

โœ… Visualizing the call stack

โœ… Backtracking explained with templates

โœ… Real-world problems like permutations, combinations, and Sudoku solver

โœ… Tips to avoid common pitfalls like infinite recursion and stack overflows

๐Ÿ”„ 1๏ธโƒฃ What is Recursion?

Recursion is when a function calls itself to solve a smaller sub-problem.

๐Ÿ”น Classic Example: Factorial

 def factorial(n): if n == 0: return 1 return n * factorial(n - 1) 
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Think in 3 parts:

1. Base case โ€“ when to stop 2. Recursive case โ€“ how the problem shrinks 3. Stack โ€“ Python uses a call stack to track function calls 
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Visualizing the Call Stack

 factorial(3) => 3 * factorial(2) => 2 * factorial(1) => 1 * factorial(0) => 1 
Enter fullscreen mode Exit fullscreen mode

Each recursive call is paused until the next one returns. This LIFO behavior is similar to a stack.

๐Ÿงฉ 2๏ธโƒฃ What is Backtracking?

Backtracking is a strategy to solve problems by exploring all possibilities and undoing decisions when needed.

Itโ€™s used when:

1. Youโ€™re generating permutations or combinations 2. Solving constraint problems (like Sudoku) 3. Exploring paths in a grid or tree 
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ง 3๏ธโƒฃ Backtracking Template

 def backtrack(path, choices): if goal_reached(path): results.append(path) return for choice in choices: if is_valid(choice): make_choice(choice) backtrack(path + [choice], updated_choices) undo_choice(choice) 
Enter fullscreen mode Exit fullscreen mode

This is the core idea behind all backtracking solutions.

๐Ÿงช 4๏ธโƒฃ Example: Generate All Permutations

 def permute(nums): results = [] def backtrack(path, remaining): if not remaining: results.append(path) return for i in range(len(remaining)): backtrack(path + [remaining[i]], remaining[:i] + remaining[i+1:]) backtrack([], nums) return results print(permute([1, 2, 3])) 
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ 5๏ธโƒฃ Example: N-Queens Problem

Place N queens on an Nร—N chessboard so that no two queens threaten each other.

 def solve_n_queens(n): solutions = [] def backtrack(row, cols, diag1, diag2, board): if row == n: solutions.append(["".join(r) for r in board]) return for col in range(n): if col in cols or (row + col) in diag1 or (row - col) in diag2: continue board[row][col] = 'Q' backtrack(row + 1, cols | {col}, diag1 | {row + col}, diag2 | {row - col}, board) board[row][col] = '.' board = [["."] * n for _ in range(n)] backtrack(0, set(), set(), set(), board) return solutions 
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ข 6๏ธโƒฃ Example: Combinations

 def combine(n, k): results = [] def backtrack(start, path): if len(path) == k: results.append(path[:]) return for i in range(start, n + 1): path.append(i) backtrack(i + 1, path) path.pop() backtrack(1, []) return results 
Enter fullscreen mode Exit fullscreen mode

โœ… Backtracking often involves modifying state, recursing, and then undoing that change.

๐ŸŽฒ 7๏ธโƒฃ Example: Solving a Sudoku Board

 def solve_sudoku(board): def is_valid(r, c, val): for i in range(9): if board[r][i] == val or board[i][c] == val or board[r//3*3 + i//3][c//3*3 + i%3] == val: return False return True def backtrack(): for r in range(9): for c in range(9): if board[r][c] == ".": for num in map(str, range(1, 10)): if is_valid(r, c, num): board[r][c] = num if backtrack(): return True board[r][c] = "." return False return True backtrack() 
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Œ A great example of recursive state search with constraint pruning.

๐Ÿง  8๏ธโƒฃ Tips and Best Practices

โœ… Always define a base case

โœ… Use sets or visited arrays to avoid cycles

โœ… Use path[:] or path.copy() when passing lists

โœ… Try to write recursive + backtracking templates once and reuse

โš ๏ธ Be careful with Python's recursion limit (sys.setrecursionlimit())

๐Ÿงช Classic Problems to Practice

Problem Type
Fibonacci Recursion + Memoization
Permutations Backtracking
N-Queens Backtracking + Pruning
Sudoku Solver Backtracking
Word Search in Grid DFS + Backtracking
Letter Combinations of a Phone Number Backtracking
Subsets / Combinations Backtracking

โœ… Summary

โœ”๏ธ Recursion is calling a function within itself to break problems into sub-problems
โœ”๏ธ Backtracking is about exploring, committing, and undoing choices
โœ”๏ธ Use backtracking for problems involving all possible combinations/permutations
โœ”๏ธ Python makes recursion intuitive with simple syntax โ€“ just be mindful of stack depth
โœ”๏ธ Think in terms of state, choices, and constraints
๐Ÿ”œ Coming Up Next:

๐Ÿ‘‰ Part 6: Sorting Algorithms โ€“ From Bubble Sort to Merge Sort (with Python Code and Complexity Analysis)

Weโ€™ll cover:

1. Selection, Bubble, Insertion Sort 
  1. Merge Sort and Quick Sort

  2. Built-in sort and Timsort

  3. When to use what

Enter fullscreen mode Exit fullscreen mode




๐Ÿ’ฌ Have a recursion problem thatโ€™s bugging you? Or a backtracking trick to share? Drop it in the comments and letโ€™s solve it together! ๐Ÿง ๐Ÿ

Top comments (0)