今天在力扣上看到一道简单的题–斐波那契数列,所以小周周就想着去搞定他练练手,结果大意了,哈哈哈,好尴尬啊。不知道大家在刷的时候会不会像小周周一样尴尬。算了,丑事不回顾了,下面让我们来看看这道题吧。
问题描述:写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
问题分析:通过描述,我们不难发现斐波那契数列的某一项等于他前面两项之和,例如f(2)=f(1)+f(0)=1; f(3)=f(2)+f(1)=2;所以我们得到的递归方程如下F(N) = F(N - 1) + F(N - 2),然后f(0)=0,f(1)=1 是我们的递归出口,分析到这里,我们就不难联想到小周周前面给大家介绍的分治算法递归策略,下面我们就来看看分治算法递归策略解斐波那契数列的代码吧。
**注意:**这段代码在力扣上是提交通过不了的,因为花费的时间很多,有大量需要重复计算的数据。
import java.util.*;
public class Fibona {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("请输入要求斐波那契数列的哪一项:");
int n = s.nextInt();
int x = feibo(n);
System.out.println("斐波那契数列的第"+n+"项是:"+x);
}
public static int feibo(int n) {
int []a = {
0};
if(n==0) {
return 0;
}else if(n==1) {
return 1;
}else {
a[0]=feibo(n-1)+feibo(n-2);
return (int)(a[0] % (Math.pow(10, 9)+7));
}
}
}
既然在力扣上使用分治算法递归策略不能解决,那难道就没有什么优化的算法解决吗?
不急,其实是有的,那就是动态规划解决,这道题使用动态规划解决再适合不过了。那什么是动态规划算法呢?
动态规划与分治法类似,也是将待求解问题分解成若干子问题,先解决子问题,再结合子问题的解得到原问题的解。
动态规划与分治算法的两者不同之处在于,动态规划的子问题不是相互独立的,而分治算法的子问题是相互独立的,这就表明采用分治算法的话,如果分解的子问题太多的话,每个都要求解,而有些子问题是一样的,那么就会重复解决了,也就是重复计算了这些相同的子问题,这就导致了很多不必要的重复计算。
所以为了解决这个问题,减少大量子问题重复计算,可以用一个表来记录所有已经解决过的子问题的答案,不管该问题以后是否需要使用到,只要他被计算过,就将其填入表中,后面需要用到就可以直接从表里拿出来使用,这就是动态规划法的基思想。
动态规划适合用来解最优化问题,通常分为4步解决
讲解完动态规划法了那就跟小周周一起来用动态规划分析一下斐波那契数列吧
既然求斐波那契数列的某一项的公式为:F(0) = 0, F(1) = 1,F(N) = F(N - 1) + F(N - 2), 其中 N > 1.所以当我们求F(N) 时就需要去求F(N - 1)和F(N - 2),,而求F(N - 1) 时就要去求 F(N - 2)和F(N - 3),所以我们可以发现F(N - 2)至F(1)这些项我们都重复计算了,所以我们就可以采用动态规划法的思想,只求一次F(N - 2)以下那些项,并把F(N - 2)以下那些项的答案都填进一个表中,以后需要用到时就可以直接从表里取出来而不用再去计算。
对于这道题呢,F(N) = F(N - 1) + F(N - 2),我们可以将其转化为F(N+1) = F(N ) + F(N - 1), 因为每个子答案我们都需要用到,所以我们可以采用交替的方法来保存那些答案,这样下一次使用时我们就可以直接拿来使用了。如何交替呢?看下面``a=0,b=1.
sun=(a+b); //sum表示第n项
a=b; //a表示n-2项
b=sum; //b表示n-1项
为什么要这样交替呢? 比如 0,1,1,2,3数列,1=1+0,2=1+1,3=2+1所以这样交替完之后,a就变成了第n-1项,b变成了第n项,这样下次计算f(n+1)项时就可以使用a+b了。由于b保存的是第n项,然后赋值给a,所以最后输出时直接输出a就行了。
因为最后输出的a可能超出int表示的最大范围,所以对其取余 1e9+7,(1000000007),如计算初始结果为:1000000008,请返回 1。这样就不会超出int的最大表示范围了。这里小周周采用了强制类型转化了。看代码。
import java.util.*;
public class fib {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("请输入要求斐波那契数列的哪一项");
int n = s.nextInt();
System.out.println("斐波那契数列第"+n+"项为:"+feibonaqi(n));
}
public static int feibonaqi(int n) {
int sum,a=0,b=1;
for(int i=0;i<n;i++) {
sum=a+b;
a=b;
b=sum;
}
return (int)(a % (Math.pow(10, 9)+7)); //强制转换为int型
}
}
好啦,斐波那契数列我们就分析到这了,相信你们看完之后也知道采用分治算法递归策略和动态规划法的区别了,赶快去刷一下斐波那契数列吧。
还是那句话,小手轻轻送上心 小周周不断在更新。
【无名之辈 我是谁 小小的天 也有大大的梦想】