深入理解斐波那契数列的递归实现与优化
斐波那契数列是一个经典的数学序列,其定义为:从 0 和 1 开始,后续每一项都等于前两项之和,即f(0) = 0,f(1) = 1,f(n) = f(n-1) + f(n-2)。在编程中,实现斐波那契数列的计算是一个常见的练习,而递归是实现方式之一,同时也存在优化的空间。
基础递归实现
最直接的递归实现方式如下:
// 时间复杂度O(2^n)
function fib(n) {
// 退出条件
if(n <= 1){
return n;
}
// 函数调用自己 递归
// 递归的公式
return fib(n-1) + fib(n-2)
}
console.log(fib(10));
这种实现的思路很清晰,符合斐波那契数列的数学定义。它将大的问题分解为多个类似的小问题,采用自顶向下的方式求解。然而,这种实现存在明显的缺陷:
- 时间复杂度高:达到了 O (2^n),随着 n 的增大,计算时间会急剧增加
- 重复计算严重:在递归过程中,大量的中间结果会被重复计算
- 可能导致栈溢出:依赖函数调用栈来实现递归,当 n 较大时,容易出现栈内存爆栈的情况
利用缓存优化递归
为了解决重复计算的问题,我们可以引入缓存机制,将已经计算过的结果存储起来,当再次需要时直接从缓存中获取,避免重复计算。
全局缓存实现
第一种优化方式是使用全局缓存对象:
const cache = {}; // 用空间换时间
function fib(n) {
if(n in cache){
return cache[n];
}
if(n <= 1){
cache[n] = n;
return n;
}
const result = fib(n-1) + fib(n-2);
cache[n] = result;
return result;
}
console.log(fib(100));
这里通过全局变量cache存储已经计算的结果,当需要计算fib(n)时,先检查缓存中是否存在,如果存在则直接返回,否则再进行计算并将结果存入缓存。这种方式用空间换取了时间,将时间复杂度优化到了 O (n)。
闭包缓存实现
另一种更优雅的方式是利用闭包来封装缓存,避免全局变量的使用:
// cache 闭合到函数中
const fib = (function() {
// 闭包
// 自由变量
const cache = {};
// IIFE
return function(n) {
if(n in cache){
return cache[n];
}
if(n <= 1){
cache[n] = n;
return n;
}
cache[n] = fib(n-1) + fib(n-2);
return cache[n];
}
})()
console.log(fib(100));
这里使用了立即执行函数表达式(IIFE),它具有以下特点:
- 立即执行
- 非常适合创建闭包
- 可以返回一个函数
通过这种方式,cache变量被封装在闭包中,不会污染全局作用域,同时又能在多次调用fib函数时保持缓存状态。
总结
递归是实现斐波那契数列的一种直观方式,尤其适合这种可以分解为树形结构的问题。但基础的递归实现存在效率问题,通过引入缓存机制(无论是全局缓存还是闭包缓存),可以有效解决重复计算的问题,大幅提高计算效率。