剑指 Offer 10- I. 斐波那契数列

剑指 Offer 10- I. 斐波那契数列

剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。


思路1:递归

最简单的就是递归,代码也简单,需要注意的是,有结束条件

class Solution {
    public int fib(int n) {
        if(n <= 1) return n;
        return fib(n - 1) + fib(n - 2);
    }
}

代码虽然没有错,但是却超出了时间限制,显然,题目不想让我们直接使用递归

在这里插入图片描述
这么写的弊端是,有大量的重复计算
比如:
F(5) = F(4) + F(3)
= F(3) + F(2) + F(3)
= F(2) + F(1) + F(2) + F(2) + F(1)
= F(1) + F(0) + F(1) + F(1) + F(0) + F(1) + F(0)+ F(1)
其实,在计算F(4)的时候,就已经知道了F(3)的值,就不需要再次计算后面的F(3)了

根据题目要求,要取余,需要修改代码:

return (fib(n - 1) + fib(n - 2)) % 1000000007;


思路2:迭代

不使用递归,就一个一个加
充分利用:F(N) = F(N - 1) + F(N - 2)
其实也就是:F(N - 2) + F(N - 1) = F(N)
定义三个变量:first、second、sum
first + second = sum

下一个循环的时候,
first往后移动,就是前面的secon,也就是first = second;
second往后移动,就是前面的sum,也就是second = sum;
这样,第二次循环的first、second都有值了,就可以求出第二次的sum值了
最后,返回sum即可。

需要注意的是,first、second、sum的初始值
因为循环是从i = 2开始,那么第一个sum,就是F(0) + F(1)的结果
那么,我们可以将firset = F(0) = 0、second = F(1) = 1
sum = 0或者 sum = 1都可以,因为,后面还要将其重新赋值。

class Solution {
    public int fib(int n) {
        //if(n < 0) return -1;
        if(n <= 1) return n;
        int first = 0;
        int second = 1;
        int sum = 0;
        //0, 1, 1, 2, 3, 5, 8,...
        for(int i = 2; i <= n; i++){
            sum = first + second;
            sum = sum % 1000000007;
            first = second;
            second = sum;
        }
        return sum;
    }
}

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
这句话没看明白,明明写对了,怎么计算结果不对?
对人生产生了怀疑,看了答案才知道,少了一个取余操作
sum = sum % 1000000007;


思路3:dp

在看答案的时候,看到有人使用dp,想想,确实这道题确实是将原问题分解为子问题求解,那么,尝试下dp解决问题:

1.是否为最值问题?

好像,,,没有明显的求最值
但是,F(n)也算是最大值

2.能否使用dp

  1. 原问题是否可以分解为子问题
    可以
  2. 是否有后效性
    无后效性

3.如何使用dp

  1. 确定dp含义
    dp(n) = F(n)

  2. 确定边界值
    dp(0) = 0;
    dp(1) = 1;

  3. 确定转移方程
    dp(n) = dp(n - 1) + dp(n - 2);

基于以上dp的三大问,我们尝试写下代码:

class Solution {
    public int fib(int n) {
        //边界条件判断
        if(n < 0) return -1;
        if(n <= 1) return n;
        
        int dp[] = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;

        for(int i = 2; i <= n; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
            dp[i] = dp[i] % 1000000007;
        }
        return dp[n];
    }
}

其实,说起来,迭代与dp方法很类似
dp使用的是数组
迭代定义了三个变量
可以将迭代理解为dp的优化版本,毕竟,dp使用数组这个方法是可以优化的,具体的优化方案就不说了,有兴趣的小伙伴可以自己查阅下资料。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值