递归算法分析

递归

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(n1)!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(n1)+F(n2)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,n1

  • 由题意可知最大加数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,m1),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,m1)+q(nm,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,m1)+1q(n,m1)+q(nm,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;
}

小编能力有限,如有问题欢迎大家评论,感谢大家的访问!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值