一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。求总共有多少总跳法,并分析算法的时间复杂度。
如果看到过这个题目的童鞋们,可能很快就有思路了,这个属于动态规划的典型题目。
基本思路如下:
假设n个台阶共有f(n)个跳法。那么我第一步可以跳1级,可以跳2级。
如果第一步跳1级,那么之后我有f(n-1)种跳法。
如果第一步跳2级,那么之后我有f(n-2)中跳法。
所以,得到下面的公式
一看到这个公式,很多人就会想到Finbonacci队列(兔子繁殖问题),很多人都是通过这个例子学会的递归方法。
代码如下:
<span style="font-size:18px;"><em>#include <iostream> #include <time.h> using namespace std; int step(int n) { if (n == 1) return 1; if (n == 2) return 2; return step(n-1) + step(n-2); } int main() { int n; int result; cin>>n; time_t start = clock(); result = step(n); cout<<result<<endl; time_t end = clock(); cout<<double(end - start)/CLOCKS_PER_SEC<<endl; return 0; }</em></span>
代码中有几个问题:
1 int 类型太小,可能很多时候会越界
2 没有进行参数检测。
但是运行的时候,发现n数字比较小的时候,还可以,但是如果输入30,就需要100秒了,输入100,可能需要几个小时(我没有等这个时间)
所以递归算法的时间,是几何形的增长。我感觉是的复杂度,对于复杂度不会求,后面有机会再补充。
于是就想把递归变成非递归。
计算机上面很多时候就是时间和空间的一个换算。非递归,就是从f(1)开始一步一步的推到f(n)
于是,我们得到f(0) = f(1)=1 那么
f(2) = f(0)+f(1)=2;
f(3)= f(1)+f(2) =3;
f(4) = f(2) +f(3) = 5;
可以看到f(n)需要记录两个变量f(n-1)和f(n-2)的数值,所以我们用变量a0 ,a1 分别表示,用循环替代递归
那么a2 = a0+a1;
a0 = a1;
a1 = a2;
注意上面的赋值顺序,先赋值a0,在赋值a1.
代码如下
<span style="font-size:18px;"><em>#include <iostream> #include <time.h> using namespace std; int main() { int n; int result; int a0 = 1; int a1 = 1; int a2 = 1; cin>>n; time_t start = clock(); for (int i = 2; i <= n ; i++) { a2 = a0 + a1; //cout<<"a2 "<<a2<<endl; a0 = a1; a1 = a2; } result = a2; cout<<result<<endl; time_t end = clock(); cout<<double(end - start)/CLOCKS_PER_SEC<<endl; return 0; }</em></span>
你在运行,输入大的数如50 100 也基本是瞬间完成的,他的效率应该是n级别。
所以,在理解程序上,递归简单一点,但是在效率上非递归要远远高于递归。所以需要根据自己的实际情况选择,最好都将递归改成非递归的方式。
台阶问题引出的递归和非递归的思考