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 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.
0 <= n <= 30
Don't worry about the time and space complexities of your algorithm.
F(n) = F(n - 1) + F(n - 2)
, we should return fibonacci(n - 1) + fibonacci(n - 2)
.
The core challenge of this problem is to compute the nth Fibonacci number using a recursive approach. The Fibonacci sequence is a classic example in computer science and has applications in various fields such as algorithm analysis, financial modeling, and biological settings.
Potential pitfalls include not handling the base cases correctly and not optimizing the recursive calls, which can lead to excessive computation time for larger values of n.
To solve this problem, we can start with a naive recursive approach:
This naive approach is simple but not optimal due to its exponential time complexity. Each call to the function results in two more calls, leading to a large number of redundant calculations.
To optimize, we can use memoization or dynamic programming:
/**
* Naive recursive approach to find the nth Fibonacci number.
* @param {number} n - The index of the Fibonacci sequence.
* @return {number} - The nth Fibonacci number.
*/
function fibonacci(n) {
// Base cases
if (n < 2) {
return n;
}
// Recursive case
return fibonacci(n - 1) + fibonacci(n - 2);
}
/**
* Memoized recursive approach to find the nth Fibonacci number.
* @param {number} n - The index of the Fibonacci sequence.
* @param {Object} memo - The cache to store previously computed Fibonacci numbers.
* @return {number} - The nth Fibonacci number.
*/
function fibonacciMemo(n, memo = {}) {
// Base cases
if (n < 2) {
return n;
}
// Check if result is already in the cache
if (memo[n]) {
return memo[n];
}
// Compute and store the result in the cache
memo[n] = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo);
return memo[n];
}
/**
* Dynamic programming approach to find the nth Fibonacci number.
* @param {number} n - The index of the Fibonacci sequence.
* @return {number} - The nth Fibonacci number.
*/
function fibonacciDP(n) {
if (n < 2) {
return n;
}
const fib = [0, 1];
for (let i = 2; i <= n; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
return fib[n];
}
Naive Recursive Approach: Time complexity is O(2^n) due to redundant calculations. Space complexity is O(n) due to the call stack.
Memoized Recursive Approach: Time complexity is O(n) as each Fibonacci number is computed once. Space complexity is O(n) due to the cache and call stack.
Dynamic Programming Approach: Time complexity is O(n) as each Fibonacci number is computed once. Space complexity is O(n) due to the array used to store Fibonacci numbers.
Edge cases include:
To test the solution comprehensively, consider the following test cases:
Use a testing framework like Jest or Mocha to automate these tests.
When approaching such problems:
Understanding and solving the Fibonacci sequence problem using recursion helps in grasping fundamental concepts of algorithm design and optimization. Practice and exploration of different approaches are key to mastering such problems.
For further reading and practice: