前言:函数学习中,我遇到了递归问题,经过一些练习,有所领悟,在此分享一下自己的学习心得
热身
编写程序用递归方法计算n!
阶乘用递归来做并不困难,下面展示代码
#include <stdio.h>
long fun(int n)
{
long f;
if(n==0||n==1)
f=1;
else
f=fun(n-1)*n;
return f;
}
int main()
{
int n;
long y;
printf("\ninput a integer number:\n");
scanf("%d",&n);
y=fun(n);
printf("%d!=%ld\n",n,y);
return 0;
}
下面开始分析这个程序,递归为什么叫递归,个人觉得说那么多概念都是空的,只要你明白它在函数里调用了自身,这个概念你就清楚了。调用自身需要终止,那就要设立一个判断条件。
我们计算5!,即fun(5)的返回值f,代入程序,f=fun(4)*5,,fun(4)就是n=4,fun函数的返回值,代入f=fun(3) * 4 *5,如此循环往复,直到最后f=fun(1) *120,fun(1)有值等于1,代入得120.
做到这里,应该能联想到高中的数列,初项知道,前一项与后一项的关系也知道,让你求其中一项的值,整个递推说白了就是基于这个思路。你要有个初值,和前一项与后一项的关系,才能确定一个数列(就是狭义上有规律的一组数)的任一项的值。
深入理解
理解了这一道题,我们就可以研究著名的汉诺塔问题。
Hanoi塔问题在数学界有很高的研究价值,是一个用递归方法解题的典型例子。经典题目:一块板上有三根针,即A、B、C,A针上套有64个大小不等的圆盘,大的在下,小的在上。如图5-4所示。要把这64个圆盘从A针移到C针上,每次只能移动一个圆盘,移动可以借助B针进行。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。求移动的步骤。
汉诺塔问题如果是个数列问题,那绝对很简单,高中的什么错位相减法都用不到,两边加个1就能构造出一个等比数列,然后一求就能就求出有多少次了。不过由于具体化了的应用问题,需要我们明白算法,才好解决。再从算法中抽象化递推关系和判断条件,编写程序就很容易了。
这里放我书上的代码,虽然我的书有点乱。。。
递归问题,找规律,一个的时候直接放到c(第三个针),两个的时候把a针的1个盘移到b针,再把a针的1个盘移到c针,最后把b针的1个圆盘移到c盘。
这个问题我直白点说,你先要把最底下的一个放到c针,然后再把倒数第二个放到c针,你放倒数第二个的时候,c针最底下的圆盘已经不起作用了(不能动了),这时候倒数第二个的操作其实和你放最底下的那一个的操作是一样的,因为它此时成了最底下的那一个。
看代码的时候我一开始不明白为什么要用什么printf,现在回头来看是为了显示具体操作
move函数我一开始没有看懂,现在回头来看,你把n代入个具体的值,比如3,那么它要执行三步,先不问具体参数,也就是两个move加一个printf,也就是move3实际上就是2个move2加一个printf操作,当然两个move里面的参数不同。说到这里你想想数列,这时候肯定会明白后一项是前一项的两倍再+1,首项为1,那么数列很容易算出时2的n次方-1。
现在研究具体参数,明白了之前的,你就很容易明白参数的含义,参数有什么用?参数唯一的体现就是每次的printf的输出,那么看else下面三行代码,第一行将x针的n-1个圆盘移到y针,自己代入会发现,z和y互换了位置。
为什么?从结果倒推
很容易想明白,比如我这个move函数说到底最终的目的不就是把a上面所有针放到c上,把z和y参数互换位置,不就是要把a上面的所有针放到b上?只不过由于n-1了,只是把n-1个针放到了b上
当然我觉得我的解释还是不够很全面,如果你还不是很理解,可以多看几个博主对于这个问题的分析,弄懂知识才是最重要的。
其余简单题目
斐波拉契数列
#include <stdio.h>
double f1(int n)
{
double f;
if(n==1||n==2)
f=1;
else
{
f=f1(n-1)+f1(n-2);
}
return f;
}
int main()
{
int n;
scanf("%d",&n);
printf("%.0lf\n",f1(n));
return 0;
}
年龄问题
#include <stdio.h>
double f1(int n)
{
double f;
if(n==1||n==2)
f=1;
else
{
f=f1(n-1)+f1(n-2);
}
return f;
}
int main()
{
int n;
scanf("%d",&n);
printf("%.0lf\n",f1(n));
return 0;
}
阿克马函数问题
#include <stdio.h>
int Ack(int n,int x,int y)
{ int z;
if(n==0)
z=x+1;
else
{
if(y==0)
{
if(n==1)
z=x;
if(n==2)
z=0;
if(n==3)
z=1;
if(n==4)
z=2;
}
else
z=Ack(n-1,Ack(n,x,y-1),x);
}
return z;
}
int main()
{
int x,y,t;
scanf("%d %d %d",&x,&y,&t);
printf("%d\n",Ack(x,y,t));
return 0;
}
这几道题都和热身算阶乘比较相似,没有汉诺塔问题那种具体情况,需要抽象化出递推公式的操作,建议必须掌握。
最后如果我的博客有什么错误,欢迎大家指出。也希望能和大家一起学习代码,共同成长。