Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Understanding Recursion: Base Cases and Recursive Steps

Hello! In our last lesson, we built the merge helper function, the core component responsible for combining two sorted subarrays. We noted that to implement the full Merge Sort algorithm, we first need to master the "divide" part of the "divide and conquer" strategy. This division is achieved through a powerful programming technique.

This lesson will introduce you to that technique: recursion. We will explore how a function can call itself to solve a problem by breaking it down into smaller, more manageable pieces. By the end of our session, you will be able to define any recursive process by its two essential components: a base case and a recursive step. This is a fundamental concept in computer science that we will use to build the mergeSort function in our next lesson.

1. What is Recursion?

At its core, recursion is the process of a function calling itself. Imagine you have a set of Russian nesting dolls. To find the smallest doll, you open the largest one, which reveals a slightly smaller, identical-looking doll. You repeat this process—opening the current doll to find the next smaller one—until you reach the final, solid doll that cannot be opened.

This process has two key parts:

  1. The recursive step: Opening a doll to find another, smaller doll inside. This is the repeated action.
  2. The base case: Finding the smallest, solid doll. This is the condition that stops the process.

Without a base case, you would be opening dolls forever! In programming, this is called an infinite recursion, which usually causes your program to crash. Therefore, every useful recursive function must have a condition to stop calling itself.

Let's watch a short video that introduces this idea in C.

#18 C Recursion | C Programming For Beginners

The video "C Recursion" by Programiz provides a concise introduction to the concept.

First, watch the section that explains the basic idea of a function calling itself and the problem it can cause: What is recursion?. Then, see how a simple conditional statement provides the solution: Preventing infinite recursion. This conditional check is the foundation of the base case.

2. The Anatomy of a Recursive Function

Every recursive function is defined by two components:

  1. Base Case: The simplest instance of the problem that can be solved directly, without making another recursive call. This is the stopping condition. A recursive function can have more than one base case.
  2. Recursive Step (or Recursive Case): The part of the function that moves the problem closer to the base case. It breaks the problem into a smaller version of itself and calls the function with this smaller input.

The following video from Portfolio Courses offers an exceptionally clear definition of these two parts using a classic example: calculating a factorial.

Calculate Factorial Using Recursion | C Programming Example

This segment from "Calculate Factorial Using Recursion" precisely defines our key terms.

Focus on the explanation of the two core components of a recursive solution: Recursive step and base case.

The general structure of a recursive function in C often looks like this:

return_type recursive_function(parameters) {
    if (/* this is the base case */) {
        // Solve the simplest problem directly and return a value
        return simple_value;
    } else {
        // This is the recursive step
        // 1. Modify parameters to make the problem smaller
        // 2. Call the function again with the smaller problem
        return recursive_function(smaller_parameters);
    }
}

3. Example 1: Factorial

The factorial of a non-negative integer , denoted as , is the product of all positive integers up to . For example, .

We can define this recursively:

  • Recursive Step: The factorial of any number is multiplied by the factorial of . In math, .
  • Base Case: The factorial of 1 (and 0) is 1. This stops the breakdown.

This image perfectly illustrates how calculating 6! is broken down recursively until it hits the base case, after which the results are combined back up.

This diagram shows the recursive calls for `factorial(6)` moving down until the base case `factorial(1)` is reached. The return values are then passed back up the chain of calls to compute the final result.

Let's examine the C code for this, which implements this exact logic.

Understanding Recursion in C Programming - w3resource

This article from w3resource provides a C implementation for the factorial function and offers a detailed explanation of its base and recursive cases.

Focus on the section titled "Detailed Explanation of Base Case and Recursive Case" right after the factorial code. It clearly identifies which part of the code corresponds to the base case and which corresponds to the recursive case, reinforcing the definitions we just learned.

4. Example 2: Sum of Natural Numbers

Let's apply this pattern to another problem: calculating the sum of all natural numbers from 1 to . For example, sum(4) = 4 + 3 + 2 + 1 = 10.

  • Recursive Step: The sum of numbers up to is plus the sum of numbers up to . That is, sum(n) = n + sum(n-1).
  • Base Case: The sum of numbers up to 0 is 0. If we receive 0, we can stop and return 0 immediately.

The following image labels these two parts in a C function.

A C function `nSum` that calculates the sum of natural numbers. The `if (n == 0)` block is the base condition, and the `else` block containing the call `nSum(n - 1)` is the recursive case.

Now, let's watch a video that not only shows the code but also visualizes how the computer executes these recursive calls.

#18 C Recursion | C Programming For Beginners

Let's return to the Programiz video, which uses this exact example.

First, watch the implementation section: Sum of N numbers example. Then, pay close attention to the visual trace that follows: How it works. This will help you understand how the final sum is assembled as the function calls resolve.

5. Hands-on Practice

Reading about recursion is one thing, but seeing it in action solidifies the concept. Here is the factorial function we discussed. Your task is to compile and run it, and then modify it to trace its own execution.




#include <stdio.h>

// Recursive function to calculate the factorial of a number
long long int factorial(int n) {
    // Add a printf here to see when the function is entered
    printf("Entering factorial with n = %d\n", n);

    long long int result;

    // Base case
    if (n == 0 || n == 1) {
        result = 1;
    }
    // Recursive case
    else {
        result = n * factorial(n - 1);
    }
    
    // Add a printf here to see what is being returned
    printf("Leaving factorial with n = %d, returning %lld\n", n, result);
    return result;
}

int main() {
    int num = 5;
    printf("Calculating factorial of %d...\n\n", num);
    
    long long int fact = factorial(num);
    
    printf("\nFinal result: Factorial of %d is %lld\n", num, fact);
    
    return 0;
}

Your Task:

  1. Copy, compile, and run the code above.
  2. Observe the output. Notice the "Entering..." messages stack up before any "Leaving..." messages appear. This shows the series of recursive calls.
  3. Trace the output by hand and compare it to the "Factorial Recursion Diagram" from earlier. You should see the same pattern of calls and returns.

This exercise gives you a practical feel for the program's flow and the role of the call stack in managing the state of each recursive call.

Conclusion

In this lesson, we demystified the concept of recursion. You learned that it is a powerful technique where a function calls itself to solve a problem by repeatedly breaking it down into smaller pieces.

Key Takeaways:

  • Recursion is a process where a function calls itself.
  • Every recursive function must have a base case, which is a stopping condition that can be solved without further recursion.
  • The recursive step is the part of the function that simplifies the problem and makes the recursive call with a smaller version of it.
  • Problems like calculating factorials or summing a series of numbers can be defined elegantly using this structure.

Preview of the Next Lesson:
We are now ready to complete our implementation of Merge Sort. In the next lesson, we will write the mergeSort function itself. You will see how it uses recursion to repeatedly divide the array in half until it reaches the base case (subarrays of size 1), and then uses the merge function we wrote previously to combine them back into a fully sorted array.

Can't find a good explanation? Sign up and we'll make it for you

Sign up