[数据结构学习笔记15] 斐波那契数列(Fibonacci)
Fibonacci 和递归
Fibonacci在递归界有很重要的地位。它能很好的说明递归是怎么方便的解决问题的,也能很好的说明递归也不是万能的,它是有限制的,那么,怎么消除这些限制,我们将用Fibonacci的不同实现理解这些问题。
什么是Fibonacci数列
fibonacci(0) = 0 fibonacci(1) = 1 fibonacci(2) = 1 // fibonacci(1) + fibonacci(0) fibonacci(3) = 2 // fibonacci(2) + fibonacci(1) fibonacci(4) = 3 // fibonacci(3) + fibonacci(2) . . . fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2)
这是个典型的递归,自己调用自己,并且有终止条件。
function fibonacci(n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } console.log('Result is', fibonacci(10));
来看看函数是怎么调用的
input: 3
fibonacci(3) -> return fibonacci(2) + fibonacci(1)
-> return fibonacci(1) + fibonacci(0) + return 1
-> return 1 + return 0 + return 1
-> 2
目前为止,看起来还不错,是我们想要的递归调用,并且似乎还比较简洁。我们继续。
input: 5
fibonacci(5) -> return fibonacci(4) + fibonacci(3)
-> return fibonacci(3) + fibonacci(2) + return fibonacci(2) + fibonacci(1)
-> return fibonacci(2) + fibonacci(1) + return fibonacci(1) + fibonacci(0) + return fibonacci(1) + fibonacci(0) + return 1
-> return fibonacci(1) + fibonacci(0) + return 1 + return 1 + return 0 + return 1 + return 0 + return 1
-> return 1 + return 0 + return 1 + return 1 + return 0 + return 1 + return 0 + return 1
-> 5
这里我们开始看到一些重复计算了。随着input越来越大,重复计算会成指数级增长,这对时间和内存都有极大的消耗。统计得出,当input是20的时候,会有21,891次递归函数调用,当input是24的时候,这个数字会达到150,049次,所以这个增长速度是指数级的。(O(2^n))。我们必须要避免这样的情况。
在讨论解决方法之前,我们插一下,为什么函数调用是昂贵的?
函数调用,特别是长时间运行的递归调用,可能是会非常消耗时间和内存的:
1. 函数调用需要额外的内存,用来存储当前调用栈里的程序状态,包括所有变量,如果递归调用嵌套很深,可能会消耗大量内存
2. 函数调用会增加处理时间,每次函数调用都要把当前状态放到调用栈,函数返回的时候,又要从栈顶弹出,函数调用越多,这个时间也会越长
3. 无止境的函数递归调用会导致栈溢出
所以能减少函数调用,那就能提高程序运行的时间。
解决方法1:带有记忆的递归,就是把函数运行的结果保存起来,后面如果有一样的调用,那就不需要run了,直接拿结果就行。本质上就是减少函数调用。
function fibonacci(index, cache=[]) { if (cache[index]) { return cache[index]; } else { if (index == 0) { return 0; } else if (index == 1) { return 1; } else { cache[index] = fibonacci(index - 1, cache) + fibonacci(index - 2, cache); } } return cache[index]; } console.log(fibonacci(10));
这个函数,当input是20时,函数调用从21,891降到37!
解决方法2:用循环的方式,对于fabonacci有几个好处,快,简介明了。
function fibonacci(n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { let a = 0; let b = 1; for (let i = 2; i <= n; i++) { let c = a + b; a = b; b = c; } return b; } } console.log(fibonacci(10));
对比三种方法的速度:
常规递归最差,O(2^n),带记忆的递归其次,循环的方法最快!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验