递归算法
定义:直接或间接地调用自身的算法称为递归算法。用函数自身给出的定义叫递归函数。
例题:
整数划分问题
问题描述:将正整数n表示成一系列正整数之和
n = n1 + n2 + ...+nk (其中,n1>= n2 >= n3 >= … >= nk >= 1 , k >= 1)
正整数的这种表示称为正整数n的划分。正整数n的不同的划分个数称为正整数n的划分数,记作p(n)。
例如正整数6有如下11中不同的划分,所以p(6)= 11。
6;
5 + 1;
4 + 2,4+ 1 + 1;
3 + 3,3+ 2 + 1,3+ 1 + 1 + 1;
2 + 2 + 2,2+ 2 + 1 + 1,2+ 1 + 1 + 1 + 1;
1 + 1 + 1 + 1 + 1 + 1。
问题分析:在正整数n的不同划分中,将最大加数n1不大m的划分个数记作q(n,m)。可以建立f(n,m)的如下递归函数。
根据n和m的关系,考虑以下几种情况:
(1)当n= 1 时,不论m的值为多少(m> 0 ),只有一种划分即{1 };
(2)当m= 1 时,不论n的值为多少,只有一种划分即n个1,{1, 1, 1, ..., 1 };
(3)当n= m 时,根据划分中是否包含n,可以分为两种情况:
(a).划分中包含n的情况,只有一个即{n };
(b).划分中不包含n的情况,这时划分中最大的数字也一定比n小,即n的所有(n - 1 ) 划分。因此f(n,n) = 1 + f(n, n-1);
(4)当n< m 时,由于划分中不可能出现负数,因此就相当于f(n,n);
(5)但n> m 时,根据划分中是否包含最大值m,可以分为两种情况:
(a).划分中包含m的情况,即{m, { x1, x2, ..., xi } }, 其中{x1, x2, ..., xi } 的和为n- m,可能再次出现m,因此是(n- m)的m划分,因此这种划分个数为f(n-m,m);
(b).划分中不包含m的情况,则划分中所有值都比m小,即n的(m - 1 ) 划分,个数为f(n,m - 1);因此f(n,m) = f(n - m, m) + f(n, m – 1);
代码:
#include<stdio.h>
int f(int n,int m)
{
if((n< 1)||(m < 1))
return 0;
if((n== 1)||(m == 1))
return 1;
if(n< m)
return f(n,n);
if(n== m)
return f(n,m-1) + 1;
if(n> m)
return f(n,m-1) + f(n-m,m);
}
int main()
{
int n,m,result;
printf("请输入正整数n:");
scanf("%d",&n);
m= n;
result= f(n,m);
printf("%d的划分有%d种\n",n,result);
return0;
}
汉诺塔问题
问题描述:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
问题分析:首先,当有64个盘子的时候,第一个柱子犯难了,要是能把前63个盘子先移动到第二个柱子上,再把最后一个盘子直接移动到第三个柱子,再把刚才的前63个盘子从第二个柱子上移动到第三个柱子上,就可以解决第一个柱子的难处。但是此时第二个柱子就犯难了,它有63个盘子,要是能把前62个盘子先移动到第三个柱子上,再把最后一个盘子直接移动到第一个柱子,再把刚才的前62个盘子从第三个柱子上移动到第一个柱子上,也就解决了第二个柱子的问题,以此类推。
从上面分析可知把n个盘子从1座(相当第一柱子)移到3座(相当第三柱子):
(1)把1座上(n-1)个盘子借助3座移到2座。
(2)把1座上第n个盘子移动3座。
(3)把2座上(n-1)个盘子借助1座移动3座。
代码:
#include<stdio.h>
void move(int n,char a,char b)
{
printf("%d---->%c---->%c\n",n,a,b);
}
void hanoi(int n,char a,charb,char c)
{
if(n == 1)
{
move(n,a,c);
}
else
{
hanoi(n-1,a,c,b);
move(n,a,c);
hanoi(n-1,b,c,a);
}
}
int main()
{
int n;
printf("请输入要移动盘子的个数:");
scanf("%d",&n);
hanoi(n,'a','b','c');
return 0;
}