Start learning free

LESSON 291 OF 410 · MEMOIZATION

Memoization

Lesson O(n) Read the full explanation

In this video lesson we will learn about memoization and use it to improve the recursive fibonacci algorithm written in the previous lesson:


Problem - Fibonacci:

The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is,

F(0) = 0, F(1) = 1
F(n) = F(n - 1) + F(n - 2), for n > 1.

Write a recursive function with memoization that given n, returns F(n).

Example 1:

Input: n = 2
Output: 1
Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1.

Example 2:

Input: n = 3
Output: 2
Explanation: F(3) = F(2) + F(1) = 1 + 1 = 2.

Example 3:

Input: n = 4
Output: 3
Explanation: F(4) = F(3) + F(2) = 2 + 1 = 3.

Constraints:
  • 0 <= n <= 30
Note:

Your algorithm should run in O(n) time and use O(n) extra space.

STEP 1 OF 6
    Ctrl + Enter to check

    JAVASCRIPT · MEMOIZATION

    Memoization in JavaScript, explained

    Introduction

    Memoization is a powerful optimization technique used to speed up programs by storing the results of expensive function calls and reusing them when the same inputs occur again. This technique is particularly useful in recursive algorithms where the same subproblems are solved multiple times. One common example is the Fibonacci sequence, where a naive recursive approach can be highly inefficient.

    Understanding the Basics

    In a naive recursive implementation of the Fibonacci sequence, the same calculations are repeated multiple times, leading to an exponential time complexity. Memoization helps by storing the results of these calculations in a cache (usually a dictionary or an array) so that they can be reused, reducing the time complexity to linear.

    Example:

    F(5) = F(4) + F(3)
    F(4) = F(3) + F(2)
    F(3) = F(2) + F(1)
    F(2) = F(1) + F(0)
    

    Without memoization, F(3) and F(2) are calculated multiple times. With memoization, we store these results and reuse them.

    Main Concepts

    The key concept in memoization is to use a data structure to store the results of function calls. When the function is called again with the same arguments, we can return the stored result instead of recalculating it.

    Steps to Implement Memoization:

    1. Create a cache to store the results of function calls.
    2. Modify the recursive function to check the cache before performing any calculations.
    3. If the result is in the cache, return it. Otherwise, calculate the result, store it in the cache, and then return it.

    Examples and Use Cases

    Let's implement the Fibonacci sequence using memoization in JavaScript:

    // Create a cache to store the results of function calls
    const memo = {};
    
    function fibonacci(n) {
      // Check if the result is in the cache
      if (n in memo) {
        return memo[n];
      }
      
      // Base cases
      if (n === 0) {
        return 0;
      }
      if (n === 1) {
        return 1;
      }
      
      // Recursive case with memoization
      memo[n] = fibonacci(n - 1) + fibonacci(n - 2);
      return memo[n];
    }
    
    // Example usage
    console.log(fibonacci(2)); // Output: 1
    console.log(fibonacci(3)); // Output: 2
    console.log(fibonacci(4)); // Output: 3
    

    Common Pitfalls and Best Practices

    One common mistake is not initializing the cache properly or not checking it before performing calculations. Always ensure that the cache is checked first to avoid unnecessary computations.

    Best practices include using clear and descriptive variable names, and ensuring that the cache is only accessible within the scope of the function to avoid unintended side effects.

    Advanced Techniques

    For more advanced memoization, consider using a higher-order function to create memoized versions of any function. This can be particularly useful for more complex algorithms.

    function memoize(fn) {
      const cache = {};
      return function(...args) {
        const key = JSON.stringify(args);
        if (key in cache) {
          return cache[key];
        }
        const result = fn(...args);
        cache[key] = result;
        return result;
      };
    }
    
    const memoizedFibonacci = memoize(function(n) {
      if (n === 0) return 0;
      if (n === 1) return 1;
      return memoizedFibonacci(n - 1) + memoizedFibonacci(n - 2);
    });
    
    console.log(memoizedFibonacci(2)); // Output: 1
    console.log(memoizedFibonacci(3)); // Output: 2
    console.log(memoizedFibonacci(4)); // Output: 3
    

    Debugging and Testing

    When debugging memoized functions, ensure that the cache is being accessed and updated correctly. Use console logs or debugging tools to inspect the cache at different stages of execution.

    For testing, write test cases that cover both base cases and recursive cases. Ensure that the function returns correct results for a range of inputs.

    Thinking and Problem-Solving Tips

    When approaching problems that can benefit from memoization, identify the subproblems that are being solved multiple times. Think about how you can store and reuse the results of these subproblems to improve efficiency.

    Practice by implementing memoization in different algorithms, such as dynamic programming problems, to gain a deeper understanding of its applications.

    Conclusion

    Memoization is a valuable technique for optimizing recursive algorithms by storing and reusing the results of expensive function calls. By understanding and applying memoization, you can significantly improve the performance of your programs.

    Practice implementing memoization in various problems to master this technique and explore its applications in more complex algorithms.

    Additional Resources

    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free