递归
问题铺垫
一个简单的递归程序
以下程序可以通过递归实现1到100的整数求和
# include <stdio.h>
//递归求和
int f(int n){
int res;
if(n == 1){
return 1;
}else{
return n + f(n - 1);
}
}
int main(){
int a = f(100);
printf("%d", a);
return 0;
}
递归的内部机制
当在一个函数的运行期间调用另一个函数时,在运行被调函数之前,系统需要完成三件事:
- 将所有的实际参数,返回地址等信息传递给被调函数保存
- 为被调函数的局部变量(也包括形参)分配存储空间
- 将控制转移到被调函数的入口
从被调函数返回主调函数之前,系统也要完成三件事:
- 保存被调函数的返回结果
- 释放被调函数所分配的存储空间
- 依照被调函数保存的返回地址将控制转移到调用函数
当有多个函数相互调用时,按照“后调用先返回”的原则,上述函数之间信息传递和控制转移必须借助”栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就在栈顶分配一个存储区,进行压栈操作,每当一个函数退出时,就释放它的存储区,就行出栈操作,当前运行的函数永远都在栈顶位置
递归满足三个条件
-
递归必须得有一个明确的中止条件
-
该函数所处理的(剩余)数据规模必须在递减
-
这个转化必须是可解的
循环和递归?
递归:
- 便于理解(想法简单,解决特定问题)
- 速度慢
- 存储空间大
循环:
- 不易理解
- 速度快
- 存储空间小
汉诺塔问题实现
思路:
如果是一个盘子,
直接将A状态上的盘子直接移动至C状态
否则,
1.先将A状态上的n-1个盘子借助C状态移动到B状态
2.直接将A状态上的盘子从A状态移动到C状态
3.最后将B状态上的n-1个盘子借助A状态移动到C状态
在这里用ABC状态来说明问题而不是ABC柱子是因为在移动中某个柱子并不会固定的成为源、中间、目标,而是会不断动态变换,然而我们将其理解为三种状态:源、中间、目标。
# include <stdio.h>
/*
如果是一个盘子,
直接将A状态上的盘子直接移动至C状态
否则,
1.先将A状态上的n-1个盘子借助C状态移动到B状态
2.直接将A状态上的盘子从A状态移动到C状态
3.最后将B状态上的n-1个盘子借助A状态移动到C状态
*/
int count = 0;
//柱子编号是逻辑上的,每次调用会变化,A为源,B为中间过程,C为目标
int Hanoi_Tower(int n, char A, char B, char C){
if(n == 1){
count++;
printf("%d.将编号为%d的盘子直接从%c柱子移动到%c柱子\n", count, n, A, C);
}
else{
//1.将n-1个从A借助C移动到B
Hanoi_Tower(n - 1, A, C, B);
//2.直接将A剩下的一个移入目标柱子
count++;
printf("%d.将编号为%d的盘子直接从%c柱子移动到%c柱子\n", count, n, A, C);
//3.将剩下的n-1个盘子从B借助A移动到C
Hanoi_Tower(n - 1, B, A, C);
}
}
int main(){
char ch1 = 'A';
char ch2 = 'B';
char ch3 = 'C';
int n;
printf("请输入要移动的盘子个数:\n");
scanf("%d", &n);
printf("要移动的盘子个数为:%d\n", n);
Hanoi_Tower(n, 'A', 'B', 'C'); //n个盘子从A借助于B移动到C
printf("总共移动了%d次!\n", count);
return 0;
}
运行结果: