定义:
一个函数自己直接或间接调用自己
当在一个函数的运行期间调用另一个函数时,在运行被调函数之前,系统需要完成三件事:
1、将所有的实际参数,返回地址等信息传递给被调函数保存
2、为被调函数的局部变量(包括形参)分配存储空间
3、将控制转移到被调函数的入口
从被调函数返回主调函数之前,系统也要完成三件事:
1、保存被调函数的返回结果
2、释放被调函数所占的存储空间
3、依照被调函数保存返回地址将控制转移到调用函数
多个函数被调用的时候,按照“后调用先返回”的原则,上述函数之间信息传递和控制转移必须借助“栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就在栈顶分配一个存储区,进行压栈操作,每当一个函数退出时,就释放它的存储区,进行出栈操作,当前运行的函数永远都在栈顶位置
A函数调用A函数和A函数调用B函数在计算机看来是没有任何区别的,只不过用我们日常的思维方式理解比较怪异而已
递归必须满足的三个条件:
1、递归必须有一个明确的终止条件
2、该函数所处理的数据规模必须在递减
3、这个转化必须是可解的
循环和递归:
递归:
易于理解
速度慢
存储空间大
循环
不易理解
速度快
存储空间小
递归调用的应用:
树和森林就是以递归的方式定义的
树和图的很多算法都是以递归来实现的
很多数学公式就是以递归方式来实现的
斐波那契序列:
1 1 2 3 5 8 13 21 34.....
利用递归求阶乘
#include <stdio.h>
#include <stdlib.h>
long mul(long n)
{
if (1 == n)
return 1;
else
return n * mul(n - 1);
}
int main(void)
{
long val, i;
printf("请输入一个数字i = ");
scanf_s("%d", &i);
val = mul(i);
printf("阶乘为%d \n", val);
system("pause");
return 0;
}
汉诺塔的实现
伪算法:
if(n > 1)
{
先把A柱子上的前n-1个盘子从A借助C移到B
将A柱子上的第n个盘子直接移到C
再将B柱子上的n-1个盘子借助A移到C
}
如果只有 1 个盘子,则不需要利用B塔,直接将盘子从A移动到C。
如果有 2 个盘子,可以先将盘子1上的盘子2移动到B;将盘子1移动到C;将盘子2移动到C。这说明了:可以借助B将2个盘子从A移动到C,当然,也可以借助C将2个盘子从A移动到B。
如果有3个盘子,那么根据2个盘子的结论,可以借助c将盘子1上的两个盘子从A移动到B;将盘子1从A移动到C,A变成空座;借助A座,将B上的两个盘子移动到C。
以此类推,上述的思路可以一直扩展到 n 个盘子的情况,将将较小的 n-1个盘子看做一个整体,也就是我们要求的子问题,以借助B塔为例,可以借助空塔B将盘子A上面的 n-1 个盘子从A移动到B;将A最大的盘子移动到C,A变成空塔;借助空塔A,将B塔上的 n-2 个盘子移动到A,将C最大的盘子移动到C,B变成空塔…
根据以上的分析,不难写出程序:
#include <stdio.h>
#include <stdlib.h>
void hannuota(int n, char A, char B, char C)
{
/*如果是一个盘子,直接将A柱子上的盘子移到C
否则 先将A柱子上的n-1个盘子借助C移到B
直接将A柱子上的盘子从A移到C
最后将B柱子上的n-1个盘子借助A移到C*/
if (1 == n)
printf_s("将编号为%d的盘子直接从%c柱子移到%c柱子\n", n, A, C);
else
{
hannuota(n - 1, A, C, B);
printf_s("将编号为%d的盘子直接从%c柱子移到%c柱子\n", n, A, C);
hannuota(n - 1, B, A, C);
}
}
int main(void)
{
char ch1 = 'A';
char ch2 = 'B';
char ch3 = 'C';
int n;
printf_s("请输入要移动盘子的个数:");
scanf_s("%d", &n);
hannuota(n, 'A', 'B', 'C');
system("pause");
return 0;
}