题目描述
现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
有一道跳台阶的与本题相似,只是初始条件不同。(关于青蛙跳台阶那题,我在牛客先做的,因此那个题解的思考过程可能比本篇详细)
斐波那契数列的初始条件是f(0)=0,f(1)=1
f(2)=f(0) + f(1) 从角标2就可以开始递推,而跳台阶(只能跳1阶或2阶)的方式,递推是从f(3)开始,因此除了初始条件不同,思路完全可以借鉴
思路1:暴力解法,标记出初始条件后,就可以进行递归调用了。
public class Solution { public int Fibonacci(int n) { if (n < 1) { return 0; } if (n == 1 ) { return n; } return Fibonacci(n - 1) + Fibonacci(n - 2); } }
思路2:可以看到递归是反复调用的,有些值可能会重复计算,f(n-1)和f(n)在调用递归时,都会用到f(n-2),此时f(n-2)就会重复计算,为了避免反复进行重复计算,我们对已经计算过的值可以利用数组存起来,需要用的时候,直接调值即可
public class Solution { public int Fibonacci(int n) { int[] arr = new int[n+1];//创建数组赋值 为了n=0时避免数组越界,因此+1 for(int i =0;i<=n;i++){ //给数组赋初值,之后检查,判断仍为初始值,允许调用递归 arr[i]=-1; } return fib(n,arr); } public int fib(int n,int[] arr){ if (n < 1) { return 0; } if (n == 1 ) { return n; } if(arr[n] == -1 ){//如果等于,说明没算过,允许递归调用计算,并将结果赋值 arr[n] =fib(n - 1,arr) + fib(n - 2,arr); } return arr[n]; //如果不等于,说明已经计算过,直接返回。避免再次调用递归计算 } }
3.思路3 动态规划,自下而上的思路,从小推导到大
数组实现方式:
public class Solution { public int Fibonacci(int n) { int[] arr = new int[n+1];//创建数组,这里可以不用赋值,如果没记错,java默认创建的都是0 return fib(n,arr); } public int fib(int n,int[] arr){ if(n==0) return arr[0]=0; //上面防了,这里也要防越界,当n为0时不能给角标为1的赋值 if(n==1) return arr[1]=1; //arr[0]其实可以先赋值,区别不大,但arr[1]必须保证n不为0的情况才能赋值 arr[0]=0; arr[1] =1; for(int i=2;i<=n;i++){ //这里直接计算就好了 arr[i] = arr[i-1]+arr[i-2]; } return arr[n]; } }
不用数组的动态规划,上面需要开辟新数组,空间复杂度高了,可以只用三个变量就可以完成,代码如下
public class Solution { public int Fibonacci(int n) { int first = 0; int second = 1; int result = 0; if(n==0) return 0;//return first; if(n==1) return 1;//return second; for(int i = 2; i <= n; i++) { result = second+first; //例如f(5)=f(4)+f(3) first = second; //重新赋值,代替原本f(3)位置的数 second = result; //重新赋值,代替原本f(4)位置的数 } return result; } }