斐波拉契数列
题目来源:LeetCode
1、题目表述
写一个函数,输入 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
示例:
2、思路解析:
-
1、 思路一:递归
递归是比较常见的解题方式,其思路就是先利用性质求出转移方程式:fn=f(n-1)+f(n-2),将求解fn分解为求f(n-1)和f(n-2),利用递归的优点在进行子问题拆分,待递归达到停止条件n==1||n ==2开始回溯,反向求解。
当在求第三项时,还得求出第一项和第二项,求N时后边的前N-1项还得重复求出,时间复杂度时2^n.
当n很大时,会产生大量的重复元素,继而会产生大量的重复的方法调用,而每一次方法的调用,虚拟机都会在内存栈中重新分配空间以保存参数、返回地址和临时变量,而每个栈的容量是有限的,当递归调用层级太多时,会产生栈溢出,所以对于测试n=10,100,500,1000时,普通循环的时间消耗要明显小于递归实现。对于n很大时应该采用普通循环来计算斐波那契数列的值。
但是递归的优点就是理解起来比较简单但是对于N越大其效率是成指数式增长,所以对于N比较大时递归方式就不再适用了。 -
2、 思路二:dp
分析前边的递归解题方法其解题过程中有着大量的重复计算,所以其效率随着N的增加就增加更快。
3、代码实现
递归版:
int fib(int n) {
if(n==1||n==2){
return 1;
}
return fib(n-1)+fib(n-2);
}
递归的停止条件就是当n1||n2就直接返回,然后开始回溯,反向求解。对于递归方式N很小时还可以运算,但是当N超过一定的数据范围,调度的时间就很长了。
时间复杂度为指数级别
dp版;
class Fibonacci {
public:
int getNthNumber(int n) {
vector<int> num(n+1,0);
for(int i=2;i<=n;i++){
num[i]=num[i-1]+num[i-2];
}
return num[n];
}
};
时间复杂度为O(N),空间复杂度为O(N);
但是这个代码额外的开辟了空间,所以可以在改进一下
class Fibonacci {
public:
int getNthNumber(int n) {
if(n==0||n==1){
return 1;
}
long long fb;
long long f0=1;
long long f1=1;
for(int i=2;i<=n;i++){
fb=f1+f0;
f0=f1;
f1=fb;
}
return fb;
}
};
既然要求第n项的斐波拉契数列所以就不用保存前n-1的数值,将每次求得第i项的数值保存在fb中然后更新f1和f2
空间复杂度为O(1),时间复杂度为O(N)。
疯狂青蛙跳台阶
题目来源:Leetcode
1、问题描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级…N级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
示例:
2、思路解析
3、代码实现
int numWays(int n) {
if(n==0){
return 1;
}
int ret=1;
for(int i=2;i<=n;i++){
ret*=2;
}
return ret;
}
if(n==0){
return 1;
}
return 1《《(n-1);
}
根据转移方程式写代码就比较简单了,但是分析过程比较复杂。