GGistDev

Recursion in Go

Recursion is a function calling itself to solve subproblems. Ensure a base case and progress toward it.

Basics

A recursive function must have a base case that stops the recursion and a step that moves toward it.

func fact(n int) int {
    if n <= 1 { return 1 }        // base case
    return n * fact(n-1)          // progress
}

Fibonacci (memoized)

The naive recursive Fibonacci is exponential; memoization turns it into linear time by caching results.

func fib(n int) int {
    if n < 2 { return n }
    return fib(n-1) + fib(n-2)
}

// Better with memoization
func fibMemo() func(int) int {
    cache := map[int]int{}
    var f func(int) int
    f = func(n int) int {
        if n < 2 { return n }
        if v, ok := cache[n]; ok { return v }
        v := f(n-1) + f(n-2)
        cache[n] = v
        return v
    }
    return f
}

Tree traversal

Trees and graphs are natural fits for recursion. Always guard against nil pointers.

type Node struct {
    Val   int
    Left  *Node
    Right *Node
}

func Sum(root *Node) int {
    if root == nil { return 0 }
    return root.Val + Sum(root.Left) + Sum(root.Right)
}

Tail recursion

  • Go does not guarantee tail-call optimization; prefer loops for deep recursions
func factIter(n int) int {
    res := 1
    for n > 1 { res *= n; n-- }
    return res
}

When to use

  • Natural divide-and-conquer (trees, DFS, permutations)
  • When iterative code is significantly less clear

Cautions

  • Stack limits: avoid extremely deep recursion
  • Memoize or convert to iterative algorithms for performance when needed
  • Beware shared state across recursive calls; pass parameters explicitly

Summary

  • Always define a base case and ensure progress
  • Prefer iterative or memoized approaches for performance and depth safety