递归是个很自然的东西,理解它其实并不难,面对要用递归的实际问题,难点就是构建递归关系、确定递归边界;
递归的定义如下:
递归:
参见“递归”。
什么?这个定义什么也没说啊!好吧,就是如下:递归:如果还没理解递归是什么意思,参见“递归”。
看到这里或许你就明白了,递归就是自己调用自己。
举个不恰当的例子:
你没钱了,找你爸要钱,爸:我没钱,找你妈要;于是你找到你妈。妈:我没钱,找你爸要。于是你有回到了你妈这里。接下来,就不难想到了,你往返你爸你妈之间,没有出口。这叫做无限递归(Infinite Recursion),尽管在这里,你爸并没有让你来找他,但还是回到他这里。换句话说,“间接地调用自己”也算递归。
数学函数也可递归定义。列如,阶乘函数f(n) = n!可也定义为:f(0) = 1, f(n) = f(n - 1) * n [n >= 1]
对应程序如下:
#include<stdio.h>
int f(int n)
{
return n == 0 ? 1 : f(n - 1) * n;
}
int main()
{
printf("%d\n", f(10));
return 0;
}
函数
可以直接或间接地调用自己。但要注意为递归函数编写终止条件,否则将产生无限递归。
排队购票案例
一场球赛开始前,售票工作正在紧张进行中。每张球票为50元,有m+n个人排队等待购票,其中有m 个人手持50元的钞票,另外n个人手 持100元的钞票。求出这m+n个人
排队购票,使售票处不至出现找不开钱的局面的不同排队种数 。
(约定:开始售票时售票处没有零钱,拿同样面值钞票的人对换位置为同一种排队。)
设计要点:
令f(m,n)表示有m个人手持50元的钞票,n个人手持100元的钞票时共有的排队总数。 分以下3种情况来讨论。
(1) n=0 n=0意味着排队购票的所有人手中拿的 都是50元的钱币,注意到拿同样面值钞 票的人对换位置为同一种排队,那么这 m个人的排队总数为1,即f(m,0)=1。
(2)m<n 当m<n时,即购票的人中持50元的人数 小于持100元的钞票,即使把m张50元 的钞票都找出去,仍会出现找不开钱的 局面,这时排队总数为0,即f(m,n)=0。
(3)其它情况 ① 第m+n个人手持100元的钞票,则在他之前的 m+n-1个人中有m个人手持50元的钞票,有n-1个人 手持100元的钞票,此种情况共有f(m,n-1)。 ② 第m+n个人手持
50元的钞票,则在他之前的 m+n-1个人中有m-1个人手持50元的钞票,有n个人 手持100元的钞票,此种情况共有f(m-1,n)。 由加法原理得到f(m,n)的递归关系:
f(m,n)=f(m,n-1)+f(m-1,n) 初始条件: 当m<n时,f(m,n)=0 当n=0时,f(m,n)=1
程序如下:
#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
int f1(int m, int n);
int main()
{
int m, n;
printf("Enter m and n: ");
scanf("%d%d", &m, &n);
printf("%d\n", f1(m, n));
return 0;
}
int f1(int m, int n)
{
int sum;
if (m < n)
{
sum = 0;
}
else if (n == 0)
{
sum = 1;
}
else {
sum = f1(m, n - 1) + f1(m - 1, n);
}
return sum;
}
汉诺塔问题(经典递归思想)
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺
序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之
间一次只能移动一个圆盘。
从左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:
一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数。
算法:当只有一个盘子的时候,只需要从将A塔上的一个盘子移到C塔上。
当A塔上有两个盘子是,先将A塔上的1号盘子(编号从上到下)移动到B塔上,再将A塔上的2号盘子移动的C塔上,最后将B塔上
的小盘子移动到C塔上。
当A塔上有3个盘子时,先将A塔上编号1至2的盘子(共2个)移动到B塔上(需借助C塔),然后将A塔上的3号最大的盘子移动
到C塔,最后将B塔上的两个盘子借助A塔移动到C塔上。
当A塔上有n个盘子是,先将A塔上编号1至n-1的盘子(共n-1个)移动到B塔上(借助C塔),然后将A塔上最大的n号盘子移动
到C塔上,最后将B塔上的n-1个盘子借助A塔移动到C塔上。
综上所述,除了只有一个盘子时不需要借助其他塔外,其余情况均一样(只是事件的复杂程度不一样)。
代码如下:
#include<stdio.h>
int i = 1;
void move(int n, char from, char to)
{
printf("第%d步: 将%d号盘子从%c移到%c\n", i++, n, from, to);
}
void hanoi(int n, char A, char B, char C)
{
if(n == 1) move(1, A, C); //只有一个盘子是直接将初塔上的盘子移动C塔;
else
{
hanoi(n - 1, A, B, C); //先将A塔的前n-1个盘子借助C塔移动到B塔上;
move(n, A, C); //将A塔剩下的一个盘子移动到C上;
hanoi(n - 1, B, A, C); //最后将塔B上的n-1个盘子借用A塔移动到目的塔C上;
}
}
int main()
{
int n;
char x='A',y='B',z='C';
printf("请输入盘子的个数:\n");
scanf("%d",&n);
printf("盘子移动情况如下:\n");
hanoi(n,x,y,z);
return 0;
}