斐波那契数列递归算法优化

一、前言

斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368…

学习递归时用的第一个简单的例子就是斐波那契数列,当时也惊叹于递归的魔力。后来在一次面试过程中,面试官就出了这道题,我还寻思怎么这么简单,当面试官让我测试用例输入100的时候,我就知道我还是太年轻了…我一直认为毫无问题的看起来既简洁又优雅的算法,居然是这么耗时的,简直毫不实用。

1 public int fib(int n) {
2     if (n == 1 || n == 2) {
3         return 1;
4     }
5     return fib(n - 2) + fib(n - 1);
6 }

我们会发现f(n)这个方法被调用了很多次,而且被重复计算了很多次。其时间复杂度为O(2^n),是指数级的,其空间复杂度为O(n)。


二、正文

1、动态规划法:把每一个计算出来的数据,用一个数组保存,需要最终值时直接从数组中取即可,避免重复计算。有一个for循环,其时间复杂度为O(n),开辟一个长度为n的数组,所以空间复杂度也为O(n)

1 public int fib(int n) {
2     int[] fib = new int[n];
3     fib[0] = 1;
4     fib[1] = 1;
5     for (int i = 2; i < n; i++) {
6         fib[i] = fib[i - 2] + fib[i - 1];
7     }
8     return fib[n - 1];
9 }

2、迭代法: 尽管上述算法已经很高效了,但我们还是会发现一个问题,其实整个数组中,每次计算时都只需要最新的3个值,前面的值计算完后就不再需要了。所以我们还可以改进,通过3个变量来存储数据。时间复杂度仍然为O(n),而空间复杂度为常量级别3,即空间复杂度为0

 1 public int fib(int n) {
 2     int first = 1;
 3     int second = 1;
 4     int third = 2;
 5     for (int i = 3; i <= n; i++) {
 6         third = first + second;
 7         first = second;
 8         second = third;
 9     }
10     return third;
11 }

3、尾递归法:通过两个变量保存计算值,传递给下一次进行计算,递归的过程中也是根据n值变化逐步重复运算,和循环差不多,时间复杂度和空间复杂度也都一样,但是比循环更简洁,更优雅

1 public int fib(int n, int first, int second) {
2     if (n <= 1) {
3         return first;
4     } else {
5         return fib(n-1,second,first+second);
6     }
7 }

4、公式法:求斐波那契数列值有一个公式…
在这里插入图片描述

1 public int fib(int n) {
2     double c = Math.sqrt(5);
3     return (int) ((Math.pow((1 + c) / 2, n) - Math.pow((1 - c) / 2, n)) / c);
4 }

三、总结

其实很多时候递归算法花费的时间和空间都非常大,所以要通过一定的手段去优化它;另外算法也不是说越简短越好,我们更应该追求的应该是效率,一个好的算法应该要兼顾效率和简洁,需要不断地去思考和打磨…共勉!

  • 15
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值