一.深入理解递归
1.什么是递归?
递归就是一个函数直接或者间接的调用自己。
2.函数是如何完成调用的?
2.1主调函数调用被调函数前,要做3件事
1.主调函数将所有的实参、返回地址传递给被调函数
2.为被调函数的局部变量(包括形参)分配存储空间
3.将控制转移到被调函数入口。
2.2被调函数返回主调函数前,要做3件事
1.保存被调函数的额返回结果
2.释放被调函数所占的空间
3.依照被调函数保存的返回地址将控制权转移到主调函数
举例说明:
/** 1.主调main()函数将实参5,printf("hello\n")的地址传递给被调函数保存 2.为f()的n变量分配存储空间 3.main()函数将控制权交给f()函数 */
void main()
{
f(5);
printf("hello\n");
}
/** 1.将被调函数的返回结果7保存起来 2.释放变量n所占的空间 3.根据保存的返回地址,将控制权转移到main()函数的printf("hello\n")处继续执行 */
int f(int n)
{
n=n+2;
return n;
}
3.递归满足的2个条件
1.必须有一个明确的终止条件
2.该函数所处理的问题规模必须是递减的(不能越递归问题越大,解决不了)
4.递归和循环的关系
所有的循环都可以用递归实现,反之,所有的递归不一定能用循环实现。
递归:易理解、速度慢(函数间的调用,需要耗费时间)、占空间大(函数间的调用,需要占空间)
循环:不易理解、速度快、占空间很少
二.用递归解决阶乘问题以及1-100的相加求和问题
1.用递归解决阶乘问题
n的阶乘:n*!(n-1)
n-1的阶乘:(n-1)*!(n-2)
n-2的阶乘:(n-2)*!(n-3)
. . . . . .
1的阶乘:1
#include <stdio.h>
#include <stdlib.h>
//递归求阶乘
int mult(n)
{
if(n==1)
{
return 1;
}
else
{
return n*mult(n-1);
}
}
int main()
{
int val=mult(3);
printf("%d",val);
return 0;
}
2.用递归解决1-100的相加求和问题
1-100的和:100+(1-99的和)
1-99的和:99+(1-98的和)
1-98的和:98+(1-97的和)
. . . . . .
1的和:1
#include <stdio.h>
#include <stdlib.h>
//递归解决相加求和问题
int mult(n)
{
if(n==1)
{
return 1;
}
else
{
return n+mult(n-1);
}
}
int main()
{
int val=mult(100);
printf("%d",val);
return 0;
}
三、汉诺塔问题
汉诺塔的传说不详细讲了,没有听说过的请点击这里:汉诺塔
用递归的思想解决汉诺塔问题:
1.将问题分解为原子步骤:比如只有2个盘子的时候,该怎么办
2.找到递归的出口(当只有1个盘子的时候,直接移动到C柱)
#include <stdio.h>
#include <stdlib.h>
//递归处理汉诺塔(我们假设就2个盘子,3根柱子)
void Hanoi(int n,char A,char B,char C)
{
//当A柱只有1个盘子的时候,从A柱直接移到C柱
if(n==1)
{
printf("将编号%d的盘子从%c柱移到%c柱\n",n,A,C);
}
else
{
//将编号为2的盘子从A柱借助C柱直接移到B柱
Hanoi(n-1,A,C,B);
//将编号为1的盘子从A柱移动到C柱
printf("将编号%d的盘子从%c柱移到%c柱\n",n,A,C);
//再将2号盘子从B柱借助A柱移到C柱
Hanoi(n-1,B,A,C);
}
}
int main()
{
Hanoi(3,'A','B','C');
return 0;
}
四.递归总结
用递归思想处理问题:
1.首先我们要把问题的每一步分解开,分成原子的步骤
2.我们不用关心编写的递归函数是怎样实现的,因为我们在分解为原子步骤,用递归处理的时候,本身就实现了。
3.递归的实质是将规模为n的问题转换为规模为n-1的问题,n-1的问题转换为规模为n-2的问题,最后转换为可解规模的问题。