一、引言
首先青蛙跳台阶的问题就是:1只青蛙可以一次跳1个或者跳2个台阶,那么请问它跳n个台阶有几种跳法?
这个问题,我们可以先尝试列举1,2,3,4,5...个台阶来看看有什么规律。表格如下:
台阶 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
跳法 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 |
我们不难发现,跳法这一行的数字是斐波那契数列的一个变化。
*ps:什么是斐波那契数列:后一个数字是前两个数字的和,第1和第二个数字都是1。那就是:
1,1,2,3,5,8,13,21,34,55...
递推公式为:
二、斐波那契数列
斐波那契数列是个非常典型的递归问题。我们可以尝试用递归来写代码,代码如下:
#include <stdio.h>
int fib(int x)
{
int c = 1;
if (x>2)
{
c = fib(x-1)+fib(x-2);
}
return c;
}
int main()
{
int n = 0;//告诉我们要知道第几项斐波那契数
printf("请输入项数:");
scanf ("%d",&n);
int ret = fib(n);//设置fib这个函数来求斐波那契数
printf ("第%d项斐波那契数是:%d",n,ret);//打印最终的结果
return 0;
}
但是当我们输入50甚至更大的数字之后会发现运算极其的缓慢 ,这是为什么呢?
我们可以尝试写出如下代码,来看看我们计算的时候 被运算了多少次。
#include <stdio.h>
int count = 0;//创造一个全局变量来把算了多少次f(3)记录下来。
int fib(int x)
{
int c = 1;
if (x==3)
{
count++;
}
if (x>2)
{
c = fib(x-1)+fib(x-2);
}
return c;
}
int main()
{
int n = 0;//告诉我们要知道第几项斐波那契数
printf("请输入项数:");
scanf ("%d",&n);
int ret = fib(n);//设置fib这个函数来求斐波那契数
printf ("第%d项斐波那契数是:%d\n",n,ret);//打印最终的结果
printf("%d",count);//打印count
return 0;
}
我们可以尝试跑一下代码,当n=40的时候,我们会惊奇的发现 被算了39088169次,从而导致了我们的计算速度是如此的缓慢。
那我们可以如何优化代码使我们的运算效率便达呢?
我们可以这样想,既然递归是从后往前算,我们何不如转换思路,从前往后算呢。
实际上我们可以把相邻的三个数字看成a,b,c.
先将a+b的值赋给c,然后将b的值赋给a,c的值赋给b,再将n-1。这样我们就完成了一次循环。之后的斐波那契数列何尝不是这样计算。我们给出下图,方便于理解。
那我们就可以用如下代码尝试优化:
#include <stdio.h>
int fib(int x)
{
int a = 1;
int b = 1;
int c = 1;
while (x>2)
{
c = a + b;//后一项为前两项的和
a = b;//重新赋值
b = c;
x--;
}
return c;
}
int main()
{
int n = 0;//告诉我们要知道第几项斐波那契数
printf("请输入项数:");
scanf ("%d",&n);
int ret = fib(n);//设置fib这个函数来求斐波那契数
printf ("第%d项斐波那契数是:%d",n,ret);//打印最终的结果
return 0;
}
这样就解决了计算效率的问题。
三、青蛙跳台阶问题的解法
讲完了斐波那契数列的写法,轻而易举的我们就可以稍加改动来完成青蛙跳台阶的问题。
#include <stdio.h>
int fib(int x)
{
int a = 1;
int b = 2;
int c = 3; //这里的c可以改为任何数,这里为了方便读者理解,就取了第三项的值
while (x>2)
{
c = a + b;//后一项为前两项的和
a = b;//重新赋值
b = c;
x--;
}
return c;
}
int main()
{
int n = 0; //初始化n来放置台阶数
printf("请输入台阶数:");
scanf ("%d",&n);
if (n>2)
{
int ret = fib(n);
printf("青蛙跳的方式有%d次",ret);
}
switch(n)
{
case 2:
printf("青蛙跳的方式有%d次",n);
break;
case 1:
printf("青蛙跳的方式有%d次",n);
break;
}
return 0;
}
由于前两个数字不一样我们并不能在fib函数中返回c,但是我们可以让n=1和2的时候不进入fib函数直接输出结果就行了。
四、总结
青蛙跳台阶的问题本质上就是斐波那契数列的问题,下次我们可以回归本质来看问题。
有的问题适合递归,有的并不适合,我们可以多多尝试,从而优化代码与计算量。