函数递归部分来啦!
目录
1,定义
2,递归的两个必要条件
3,递归与循环
递归和循环的互换
递归和循环的选择
实例
1,汉诺塔问题
2,青蛙跳台阶问题
话不多说,我们先上定义:
一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的。用递归过程定义的函数,称为递归函数,例如连加、连乘及阶乘等。凡是递归的函数,都是可计算的,即能行的 [1]
。
古典递归函数,是一种定义在自然数集合上的函数,它的未知值往往要通过有限次运算回归到已知值来求出,故称为“递归”。它是古典递归函数论的研究对象。
博主这里在加上几句话,帮助大家理解。
什么是递归?
程序调用自身的编程技巧成为递归(recursion)递归作为一种算法,在程序设计中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只要少量程序就可以描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。
递归的主要思考方式在于:大事化小。
1,存在限制条件,当满足这个条件是,递归便不在继续进行。
2,每次递归调用之后越来越接近这个限制条件。
备注:即便是符合这两个条件的递归,也有可能出现栈溢出的危险。
这里博主给上一个最简单的递归
#include
int main()
{
printf("hehe\n");
main();
return 0;
}
当然,这个最简单的递归很显然没有限制条件,会无限打印hehe直到栈溢出。
而栈溢出又是什么呢?
我们假设下面这一块是内存
局部变量,函数参数等 这块是栈区 |
动态开辟的内存,malloc,calloc等 这块是堆区 |
全局变量,static修饰的变量等 这块是静态区 |
而每次调用函数,都需要压栈,都需要在栈中申请空间,而空间必定是有限的,当栈空间满时,就溢出了,这也是为什么递归一定要有限制条件的原因。
即上面的代码,每调用一次main函数都会在栈区开辟一块空间供其使用。
递归和循环其实在大部分情况下是可以互换的,下面给个例子吧。
#include
//求n的阶乘
int Fac1(int n)
{
int i = 0;
int ret = 0;
for (i = 1; i <= n; i++)
{
ret *= i;
}
return ret;
}
int Fac2(int n)
{
if (n <= 1)
return 1;
else
return n*Fac2(n - 1);
}
int main()
{
int n;
scanf("%d", &n);
int ret1 = Fac1(n);
int ret2 = Fac2(n);
return 0;
}
从上面的代码我们可以清楚的感受到,递归和循环是可以互换的,递归的思想在于,想到n阶乘每次都是n乘n-1,直到有一次,n=1时,就不用再继续往下乘也就是n的阶乘等于n乘n-1的阶乘。而Fac函数不就是n的阶乘吗?那放入n-1不就是n-1的阶乘吗?就这样,大事化小,小部分代码解决这个问题
1,两者都挺容易时,选熟练拿手的,但要保证都没问题。递归有栈溢出和效率慢的可能缺点。
2,两者有一者挺容易时,选容易的,当然也要保证没问题。
接下来,来两道递归的经典问题吧。
题目
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如图1)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
这个的思想在于,如果A杆上只有一个盘,那么只需要将A杆上的盘直接移到C杆上,那么也就是,需要将A杆上的63个盘移到B杆上。当这一步完成之后,我们是否要移动第二大的盘了?就是将B杆上的62个盘移到A杆上,再将B杆上的第二大盘移到C。而后又从A杆开始新一轮的递归(循环)直到每一个盘都被移到C杆上。
代码实现如下
#include
void Hanoi(int n, char A, char B, char C)
{
if (n == 1)
printf("%c->%c\n", A, C);
else
{
Hanoi(n - 1, A, C, B);
printf("%c->%c\n", A, C);
Hanoi(n - 1, B, A, C);
}
}
int main()
{
int n;
printf("请输入要移动的盘子数:");
scanf("%d", &n);
printf("要移动的盘子数:%d\n", n);
printf("实现过程:\n");
Hanoi(n, 'A', ' B', 'C');
return 0;
}
如果A上只有一个盘子,直接从A移到C即可,如果A 上有多个,需要先将其他的盘子移到B上,然后,将A上的盘子移到C处,将B上的其他盘子再移到A上,并将B的剩下盘子移到C,一直不断重复下去,直到将A的最后一个盘子放到C上,就完成整个过程。
至于使用的printf,是为了可以方便地进行杆子之间的调换。
青蛙跳台阶,一共n个台阶,青蛙一次可以跳两个台阶,亦可以一次跳一个台阶,请问,青蛙跳到n阶一共有多少种跳法。
这道题目思路就比较明显啦:
假设青蛙现在在n阶上,由于青蛙一次只能跳一阶或者两阶,那么,青蛙可能是从n-1阶上跳上来的或者从n-2阶上跳上来,一共只有这两种情况。也就是,青蛙在n阶时,一共有两种跳法,而这两种跳法各自又有在n阶时相对应的n-1,n-2跳法.....而注意到,青蛙在一阶时,只有一个跳法,或者两阶时,只有两个跳法。也就是相应阶数,相应方法,条件就在这里产生。
代码实现如下:
#include
int jump(int n)
{
if (n <= 2)
return n;
else
return jump(n - 1) + jump(n - 2);
}
int main()
{
int n = 0;
printf("请输入青蛙要跳到的阶数:");
scanf("%d",&n);
int ret = jump(n);
printf("青蛙要跳到%d阶的方法有%d种\n", n, ret);
return 0;
}
青蛙跳台阶的问题,就这样解决啦。
以上就是今天博主分享的全部内容啦,这样我们函数部分就差不多结束啦,下一次大家想一起学一些什么呢,不如趁热打铁,说一下斐波那契数列的两种实现方法?还是想一起学一下C语言实现三子棋呢?大家可以在评论区告诉我。码字不易,看到这里,不妨点个赞,点个关注在走吧。这些问题思路的整理有些耗时,所以今天的博客就晚了一些才发布。希望大家会喜欢。
参考资料:
1,递归函数_百度百科 (baidu.com)
2,汉诺塔问题_百度百科 (baidu.com)