递归
1. 递归概念
直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数。
2. 递归常见算法分析
2.1 阶乘函数
分析
阶乘函数可递归地定义为
n
!
=
{
1
n
=
0
n
(
n
−
1
)
!
n
>
0
n!=\begin{cases} 1&n=0\\ n(n-1)!&n>0\end{cases}
n!={1n(n−1)!n=0n>0
问题
使用递归求解一个数的阶乘
输入
输入一个正整数n,n<20
输出
输出正整数n的阶乘
样例
-
样例输入
5
-
样例输出
120
代码
#include<bits/stdc++.h>
using namespace std;
int solve(int t)
{
if(t==0)
{
return 1;
}
else{
return t*solve(t-1);//调用自身函数递归求解
}
}
int main()
{
int n;
cin>>n;
cout<<solve(n);
}
2.2 Fibonacci数列
分析
无穷数列1,1,2,3,5,8,13,21,34,55,…,称为Fibonacci数列。这是一个递归关系式,它说明n大于1时,这个数列的第n项的值是它前面的两项之和。它用两个较小的自变量的函数值来定义较大自变量的函数值,所以需要两个初始的F(0)和F(1)。
Fibonacci函数可递归地定义为
F
(
n
)
=
{
1
n
=
1
,
2
F
(
n
−
1
)
+
F
(
n
−
2
)
n
>
1
F(n)=\begin{cases} 1&n=1,2\\ F(n-1)+F(n-2)&n>1\end{cases}
F(n)={1F(n−1)+F(n−2)n=1,2n>1
问题
输入一个正整数n,求Fibonacci数列的第n个数。Fibonacci数列的特点:第1,2个数为1,1。从第3个数开始,概述是前面两个数之和。要求输入的正整数n不超过50。
输入
一个不超过50的正整数。
输出
Fibonacci数列的第n个数,末尾输出换行。
样例
-
样例输入
20
-
样例输出
6765
代码
#include<bits/stdc++.h>
using namespace std;
int Fibonacci(int t)
{
if(t==1||t==2)
{
return 1;
}
else{
return Fibonacci(t-1)+Fibonacci(t-2);//递归求解
}
}
int main()
{
int n;
cin>>n;
cout<<Fibonacci(n)<<endl;
}
2.3 整数划分问题
分析
将正整数n表示为成一系列正整数之和,n=n1+n2+…+nk,其中n1>=n2>=…>=nk>=1,k>=1,正整数n的这种表示称为正整数n的划分。正整数n的不同的划分个数称为正整数n的划分数。
这里加入一个最大加数m的概念,也就是说在正整数n的划分中找出一个最大加数,比如说正整数6有一种划分为6=4+1+1,其中4就是它的最大加数。因此n表示需要划分的正整数,m表示划分时的最大加数,定义==q(n,m)==表示正整数n在最大加数为m时的划分个数。由此可得以下递归的几种情况。
-
当最大加数不大于1时,任何正整数n都只有一种划分形式,即n=1+1+1+1…;
q ( n , 1 ) = 1 , n ≥ 1 q(n,1)=1,n\geq1 q(n,1)=1,n≥1 -
由题意可知最大加数m不能大于正整数n;
q ( n , m ) = q ( n , n ) , n < m q(n,m)=q(n,n),n<m q(n,m)=q(n,n),n<m -
当最大加数m与正整数n相等时,正整数n在加数为m的划分个数为1,所以正整数n的划分个数为1加上正整数n在最大加数为m-1的和
q ( n , m ) = 1 + q ( n , m − 1 ) , n = m q(n,m)=1+q(n,m-1),n=m q(n,m)=1+q(n,m−1),n=m -
当最大加数m小于正整数n且大于1时,正整数n的划分个数为最大加数为m-1的划分个数与正整数n减去最大加数后最大加数为m的和。
q ( n , m ) = q ( n , m − 1 ) + q ( n − m , m ) , n > m > 1 q(n,m)=q(n,m-1)+q(n-m,m),n>m>1 q(n,m)=q(n,m−1)+q(n−m,m),n>m>1
以上关系实际上给出了计算q(n,m)的递归式如下
q
(
n
,
m
)
=
{
1
n
=
1
,
m
=
1
q
(
n
,
n
)
n
<
m
q
(
n
,
m
−
1
)
+
1
n
=
m
q
(
n
,
m
−
1
)
+
q
(
n
−
m
,
m
)
n
>
m
>
1
q(n,m)=\begin{cases} 1&n=1,m=1\\ q(n,n)&n<m\\ q(n,m-1)+1&n=m\\ q(n,m-1)+q(n-m,m)&n>m>1\end{cases}
q(n,m)=⎩⎪⎪⎪⎨⎪⎪⎪⎧1q(n,n)q(n,m−1)+1q(n,m−1)+q(n−m,m)n=1,m=1n<mn=mn>m>1
问题
使用递归编写一个程序,求一个正整数n的所有划分个数。
例如,输入3,输出3;输入4,输出5。
输入
多组输入,每一组是一个正整数n。
输出
输出划分数。
样例
-
样例输入
3
4 -
样例输出
3
5
代码
#include<stdio.h>
int huafeng(int n,int m)
{
if((n<1)||(m<1))
return 0;
if((n==1)||(m==1))
return 1;
if((n<m))
return huafeng(n,n);
if(n==m)
return huafeng(n,m-1)+1;
else
return huafeng(n,m-1)+huafeng(n-m,m);
}
int main()
{
int a,b,n;
while(scanf("%d",&a)!=EOF)
{
printf("%d\n",huafeng(a,a));
}
}
2.4 Hanoi塔问题
分析
设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为.2…n,如图2-1所示。现要求将塔座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置。在移动圆盘时应该遵守以下移动规则。
- 规则(1):每次只能移动1个圆盘。
- 规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上。
- 规则(3):在满足移动规则(1)和规则(2)的前提下,可将圆盘移至a,b,c中任一塔座上。
当有n个圆盘时,可以将a塔上面的n-1个较小的圆盘先看成一个整体,问题就化解为最下面的n圆盘和n-1圆盘两个圆盘的移动方法,首先将上面n-1圆盘通过b塔的辅助从a塔移动到c塔,然后将n圆盘从a塔移动到b塔,最后将n-1圆盘通过a塔从c塔移动到b塔,由此可见,n个圆盘的移动问题可分为两次n-1圆盘的移动和一次n圆盘的移动。以此类推,当求n-1个圆盘的移动方法时,也可以分为上面较小的n-2个圆盘和下面n-1的圆盘来进行上面的步骤,因此递归求解。
问题
使用递归编写一个程序实现汉诺塔问题(从a移到c),要求在输入圆盘数量之后,输出圆盘的移动步骤,输出格式示例如下:
- 第1步:1号盘从A柱移至B柱
- 第2步:2号盘从A柱移至C柱
输入
多组测试用例,每组输入一个正整数n,n代表圆盘数量。
输出
每组输出之间有一行空行。
样例
-
样例输入
3
-
样例输出
第1步:1号盘从A柱移至C柱
第2步:2号盘从A柱移至B柱
第3步:1号盘从C柱移至B柱
第4步:3号盘从A柱移至C柱
第5步:1号盘从B柱移至A柱
第6步:2号盘从B柱移至C柱
第7步:1号盘从A柱移至C柱
代码
#include<stdio.h>
int a=1;
//定义输出函数
void move(int b,char c,char d){
printf("第%d步:%d号盘从%c柱移至%c柱\n",a,b,c,d);
a++;
}
//定义递归函数 n是盘子编号(按数字盘子大小从小到大) a是被移盘子所在塔 b是辅助塔 c是目标塔
void hanoita(int n,char a,char b, char c)
{
if(n>0)
{
//将n-1号盘以c塔为辅助塔从a塔移动到b塔
hanoita(n-1,a,c,b);
move(n,a,c);
//将n-1号盘以a塔为辅助塔从b塔移动到c塔
hanoita(n-1,b,a,c);
}
}
int main(){
int n;
while(scanf("%d",&n)!=EOF)
{
hanoita(n,'A','B','C');
printf("\n");
a=1;
}
}
2.5 全排列问题
分析
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
对于需要排列的n个元素,用一个一维数组a[n]来存,对于i=1~n,每次a[1]与a[i]进行交换,然后对a[2] ~a[n]中的n-1个元素继续重复上过程进行排序。每个数字轮流做第一个,确认好第一个后,对剩下的n-1个元素再进行排序。一个排列完成后需要把交换过的数字交换回来,这样就能避免漏掉某些情况!
问题
编写一个程序,使用递归算法输出一个一维字符数组中所有字符的全排列,假设字符都不一样。例如{‘a’,‘b’,‘c’}的全排列为(a,b,c), (a,c,b), (b,a,c), (b,c,a), (c,a,b), (c,b,a)
输入
多组测试用例,每组输入一个正整数n(0<n<=26)。
输出
输出从a开始,连续n个字母的全排列,且每组输出之间用空格隔开。
样例
-
样例输入
1
2 -
样例输出
a
ab
ba
代码
#include<stdio.h>
void pailie(char a[],int k,int n)
{
int i,t;
if(k==n)
{
//递归结束,输出全排列的一种情况
for(i=0;i<n;i++)
{
printf("%c",a[i]);
}
printf("\n");
}
for(i=k;i<n;i++)
{
//轮流做第k个 第k个与第i个交换
t=a[k];
a[k]=a[i];
a[i]=t;
//对剩下的n-k个元素继续进行全排序(递归求解)
pailie(a,k+1,n);
//排序完后将交换过的交换回来
t=a[i];
a[i]=a[k];
a[k]=t;
}
}
int main()
{
int n,k;
char t;
char a[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
while(scanf("%d",&n)!=EOF)
{
pailie(a,0,n);
printf("\n");
}
return 0;
}
小编能力有限,如有问题欢迎大家评论,感谢大家的访问!