Docs
Documentation Fundamentals

Recursion

Overview

Recursion is a programming technique where a function calls itself to solve smaller subproblems of the original problem. It is commonly used in divide-and-conquer algorithms, backtracking, tree traversals, and more.

In Swift, recursive functions are straightforward to write, but they must be carefully managed to avoid stack overflow or inefficiency due to repeated work.

Key Concepts

  • Base Case: The condition that stops recursion.

  • Recursive Case: The part of the function where it calls itself.

  • Call Stack: Each recursive call is added to the call stack until the base case is reached.

Factorial Example

The factorial of a number n is defined as:

n! = n × (n - 1)!

With base case: 0! = 1

func factorial(_ n: Int) -> Int {
    if n == 0 {
        return 1
    }
    return n * factorial(n - 1)
}

Fibonacci Example

Fibonacci sequence:

0, 1, 1, 2, 3, 5, 8, ...
func fibonacci(_ n: Int) -> Int {
    if n <= 1 {
        return n
    }
    return fibonacci(n - 1) + fibonacci(n - 2)
}

Tree Traversal (Binary Tree)

class TreeNode {
    var value: Int
    var left: TreeNode?
    var right: TreeNode?

    init(_ value: Int) {
        self.value = value
    }
}

func inOrderTraversal(_ node: TreeNode?) {
    guard let node = node else { return }
    inOrderTraversal(node.left)
    print(node.value)
    inOrderTraversal(node.right)
}

Backtracking Example (N-Queens)

func solveNQueens(_ n: Int) -> [[String]] {
    var board = Array(repeating: Array(repeating: ".", count: n), count: n)
    var results: [[String]] = []

    func isValid(_ row: Int, _ col: Int) -> Bool {
        for i in 0..<row {
            if board[i][col] == "Q" ||
               (col - row + i >= 0 && board[i][col - row + i] == "Q") ||
               (col + row - i < n && board[i][col + row - i] == "Q") {
                return false
            }
        }
        return true
    }

    func backtrack(_ row: Int) {
        if row == n {
            results.append(board.map { $0.joined() })
            return
        }

        for col in 0..<n {
            if isValid(row, col) {
                board[row][col] = "Q"
                backtrack(row + 1)
                board[row][col] = "."
            }
        }
    }

    backtrack(0)
    return results
}

When to Use Recursion

  • Problems with overlapping subproblems (use memoization).

  • Data structures with recursive nature (e.g., trees, graphs).

  • Backtracking problems (e.g., permutations, combinations).

  • Divide and conquer algorithms (e.g., merge sort, quick sort).

Tips for Writing Recursive Functions

  • Always define a clear base case to prevent infinite recursion.

  • Consider memoization for optimization.

  • Convert to iteration if recursion depth is too deep.

  • Be cautious of stack overflow on large input sizes.

Summary

Recursion is a powerful tool in Swift for solving problems that can be broken down into smaller, similar problems. Mastering recursion helps you approach complex algorithms with elegance and clarity.

Docs
Copyright © Mitch Lang. All rights reserved.