• Home
  • About
    • Thoughts To Pen photo

      Thoughts To Pen

      My thoughts on Computer Programming || Psychology || Personal Finances || & much more...

    • Learn More
    • Twitter
    • Instagram
    • Github
    • StackOverflow
  • Posts
    • All Posts
    • All Tags
  • Projects
  • Portfolio
  • Resources
  • About

Recursion Patterns — Base Cases & Unwinding

16 Mar 2026

Introduction

We have been using recursion throughout this entire blog series. We used it to traverse Binary Trees, we used it in Depth-First Search, and we used it as the foundation for Dynamic Programming and Backtracking.

But recursion can still feel like black magic. How does calling a function from inside itself actually work? How does the computer keep track of where it is? Programmers often struggle with recursion because they try to hold the entire sequence in their brain at once.

In this post, we will demystify recursion by breaking down its anatomy, visualising the Call Stack, and looking at the common patterns you will encounter.


The Two Rules of Recursion

Every correctly written recursive function must have two parts:

  1. The Base Case: The condition where the recursion stops. This is the bedrock. Without this, the function will call itself infinitely until the program crashes with a StackOverflowError.
  2. The Recursive Step: The part where the function calls itself, but with a smaller or altered input that moves it one step closer to the Base Case.

A Simple Example: Countdown

Let’s write a function that prints a countdown from $n$ to 1.

public void countdown(int n) {
    // 1. The Base Case
    if (n == 0) {
        System.out.println("Liftoff!");
        return;
    }
    
    // Process current step
    System.out.println(n);
    
    // 2. The Recursive Step (moving towards n == 0)
    countdown(n - 1);
}

The Magic of the Call Stack

When countdown(3) is executed, what actually happens inside the computer? The CPU doesn’t magically jump back to the top of the code. It creates a brand new, isolated “frame” on the Call Stack.

Think of the Call Stack like a Stack of sticky notes.

  1. You call countdown(3). The computer writes n=3 on a sticky note and places it on the desk.
  2. The code prints 3, then calls countdown(2). The computer doesn’t overwrite the first note! It writes n=2 on a new sticky note and slaps it on top of the first one.
  3. The code prints 2, then calls countdown(1). New sticky note (n=1) goes on top.
  4. The code prints 1, then calls countdown(0). New sticky note (n=0) goes on top.
  5. In the countdown(0) frame, n == 0 is true. It prints “Liftoff!” and hits the return statement.
  6. The Unwinding: The computer crumples up the top sticky note (n=0). It looks at the one underneath (n=1). That function resumes exactly where it left off, finishes, and is crumpled up. This happens until the desk is clear.

Recursion Patterns

There are two main ways to structure your recursive code, and the difference determines when the work actually gets done.

Let’s look at calculating the factorial of a number ($5! = 5 \times 4 \times 3 \times 2 \times 1$).

Pattern 1: Head Recursion (Work done DURING unwind)

In Head Recursion, the recursive call happens before the main work, or the main work depends on the result of the recursive call.

public int factorialHead(int n) {
    if (n == 1) return 1; // Base case
    
    // The multiplication (the work) waits for the recursion to finish
    return n * factorialHead(n - 1);
}

Execution trace for factorialHead(3):

  • f(3) returns 3 * f(2) … it pauses to wait for f(2).
  • f(2) returns 2 * f(1) … it pauses to wait for f(1).
  • f(1) returns 1 (Base Case!).
  • Now we unwind!
  • f(2) gets the 1. It finishes calculating 2 * 1 = 2 and returns it.
  • f(3) gets the 2. It finishes calculating 3 * 2 = 6 and returns it.

The actual multiplying only happens on the way back up the stack.

Pattern 2: Tail Recursion (Work done BEFORE/DURING call)

In Tail Recursion, you pass the accumulated result forward into the next function call. The recursive call is the absolute final action in the method.

// We use a helper passing an 'accumulator' starting at 1
public int factorialTail(int n, int accumulator) {
    if (n == 1) return accumulator; // Base case
    
    // The multiplication happens NOW, and we pass the result downward
    return factorialTail(n - 1, n * accumulator);
}

Execution trace for factorialTail(3, 1):

  • f(3, 1) calculates 3 * 1 = 3, then calls f(2, 3).
  • f(2, 3) calculates 2 * 3 = 6, then calls f(1, 6).
  • f(1, 6) hits the base case and returns 6.
  • The 6 is just passed back up the chain instantly without any further math.

Why does this matter? In some languages (like C++ or Scala), the compiler recognises Tail Recursion and actually deletes the old stack frames as it goes, preventing StackOverflowErrors entirely! (Note: Java does not do this, but it is still a crucial CS concept to know).


How to Think Recursively

When tackling a recursive problem, do not try to trace the code all the way down to the base case in your head. You will get dizzy.

Instead, take a Leap of Faith. Assume your recursive function already works perfectly for the n-1 case. Your only job is to figure out: “If I already magically have the correct answer for n-1, what do I need to do to n to finish the job?”

Take Reversing a Linked List: “If the rest of the list is already magically reversed, I just need to make the node after me point back to me, and make myself point to null.”

public Node reverseList(Node head) {
    if (head == null || head.next == null) return head;
    
    // LEAP OF FAITH: Assume this perfectly reverses the rest of the list
    Node reversedRest = reverseList(head.next);
    
    // My small job: fix my immediate neighbour
    head.next.next = head;
    head.next = null;
    
    return reversedRest;
}

Practice Problems

Recursion is a muscle. The only way to get comfortable with it is to write it constantly.

  1. Fibonacci Number – LeetCode #509 — The introductory requirement.
  2. Reverse String – LeetCode #344 — Try solving this strictly using recursion without loops.
  3. Swap Nodes in Pairs – LeetCode #24 — A fantastic exercise in trusting the Leap of Faith.

What’s Next?

We have spent phase after phase looking at massive, complex data structures and abstract logic patterns. In our next (and final) post of Phase 4, we zoom all the way down to the bedrock of modern computing: manipulating the 1s and 0s directly. Welcome to Bit Manipulation.



programmingalgorithmsrecursion Share Tweet Msg