题目
斐波那契数列:
f(n)=f(n-1)+f(n-2)(n>2) f(0)=1;f(1)=1;
即有名的兔子繁衍问题
1 1 2 3 5 8 13 21 ....
我的解法
递归
public static int Recursion(int n){
if(n==1){
return 0;
}
if(n==2){
return 1;
}
return Recursion(n-1)+Recursion(n-2);
}
图片转自https://blog.csdn.net/MallowFlower/article/details/78858553
尾递归
递归时间复杂度比较高。递归不停地压栈和出栈,时间和空间都有很大的消耗,
如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。
简单理解,就是处于函数尾部的递归调用本身的情形下,前面的变量状态都不需要再保存了,可以释放,从而节省很大的内存空间。在前面的代码中,明显在调用递归调用Fibonacci(n-1)
的时候,还有Fibonacci(n-2)
没有执行,需要保存前面的状态,因此开销较大的
public int lastFibonacci(int n, int ret1, int ret2) {
if(n == 1) {
return ret1;
}
return lastFibonacci(n - 1, ret2, ret1 + ret2);
}
ret1和ret2初始传入都为1,其实跟我下面的迭代方法思路一样,无非就是递归实现。关键点在于递归调用处的ret2就是n的值。因为递归调用处的ret2是上次调用的ret1+ret2,尾部递归每次把和传给下次调用。
迭代方法
这个方法类似于爬楼梯问题,详见我的博客爬楼梯算法。
int first=1;
int second=2;
int res=0;
for(int i=3;i<=n; i++){
res=first+second;
first=second;
second=res;
}
return res;
动态规划
这道题目不是leetcode上的,主要是为了复习递归的思路和动态规划的思路,加强练习。
public static int fib(int n) {
int[] dp=new int[n+1];
dp[0]=1;
dp[1]=1;
for(int i=3;i<=n;i++){
dp[i-1]=dp[i-2]+dp[i-3];
}
return dp[n-1];
}
/* Driver program to test maxSubArraySum */
public static void main(String[] args) {
int n=8;
int res = fib(n);
System.out.println("solution is "
+ res);
}
这个题目用动态规划解决时,我们只需要最近一次的和,所以没有必需用数组记录所有的中间和,可以节省空间,利用上面的迭代方法解决,是最优的解决方式,但是,这个动态规划的基本步骤和思路在其他问题中需要记录中间值,比如最大和的子数组问题等,需要记录中间的和,比较哪个最大。