递归的典型问题

什么是递归?

程序调用自身的编程技巧称为递归。它通常把一个大型复杂的问题层层转化为一个与原问题相似规模较小的问题来求解。

递归的主要思考方式:

把大事化小!

递归的必要条件:

1.存在限制条件,当满足这个限制条件的时候,递归便不再继续
2.每次递归调用之后越来越接近这个限制条件

下面我们通过两个有趣的题来更深刻的了解递归。
一、汉诺塔问题
      题目:古代有一个梵塔,塔内有三个柱子A、B、C,A柱子上有若干个盘子,盘子大的在下,小的在上,目标是要将这若干个盘子从A柱移到C柱,但每次只能允许移动一个盘子,并且在移动过程中,3个柱子上的盘子始终保持小盘在上,大盘在下,在移动过程中可以利用其他柱子,要求打印移动的步骤和 移动的次数。
我们来分析一下这个题目,如何才能把它大事化小:
(我们用1,2,…,n表示n个盘子,称为1号盘,2号盘,…。号数大盘子就大)
      (1)当A柱子上只有一个盘子时,只需将A柱子上的一个盘子直接移到C柱子上即可;
      (2)当A柱子上有两个盘子时,先将A柱子上的1号盘子(编号从上到下)移动到B柱子 上,再将A柱子上的2号盘子移动的C柱子上,最后将B柱子上的小盘子移动到C柱子上;
      (3)当A柱子上有3个盘子时,先将A柱子上编号1至2的盘子(共2个)移动到B柱子上(需借助C柱子),然后将A柱子上的3号最大的盘子移动到C柱子,最后将B柱子上的两个盘子借助A柱子移动到C柱子上;
      (4)当A柱子上有n个盘子时,先将A柱子上编号1至n-1的盘子(共n-1个)移动到B柱子上(借助C柱子),然后将A柱子上最大的n号盘子移动到C柱子上,最后将B柱子上的n-1个盘子借助A柱子移动到C柱子上。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int count = 0;
void move(int i, char src, char dst)
{
    printf("第%d个盘子:%c----->%c\n",i,src,dst);
    count++;
}

void Hannuota(int n, char a, char b, char c)//a为盘子原来在的柱子,b为中间柱子,c为目标柱子
{
    if (n == 1)
    {
        move(n, a, c);//直接把a上的柱子移到目标柱子c上
    }
    else
    {
         Hannuota(n - 1,a, c, b);//把n-1个盘子从a移到b柱子上(借助柱子c)
         move(n, a, c);//上一步把这个盘子上面的n-1个盘子移到了柱子b上,这一步就把这第n个盘子直接移到目标柱子c上
         Hannuota(n - 1, b, a, c);//把在b柱子上的n-1个盘子移到柱子c上(借助柱子a)
    }

}

int main()
{
    int n = 0;
    char a = 'A';
    char b = 'B';
    char c = 'C';
    printf("请输入你要移动的盘子数>\n");
    scanf("%d", &n);
    Hannuota(n, a, b, c);
    printf("%d个盘子要移动%d次\n", n, count);
    system("pause");
    return 0;
}

二、青蛙爬楼梯
      题目:假设你正在爬楼梯,需要n步你才能到达顶部。但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部?
我们来分析这个问题:
1个台阶:1步——1种方法
2个台阶:2步,1+1——2种方法
3个台阶:1+1+1, 1+2, 2+1——3种方法
4个台阶:1+1+1+1, 1+1+2, 1+2+1, 2+2,2+1+1——5种方法
…………
找到规律,其实就是斐波拉契数列
或者这样分析:
1.如果起始跳一阶的话,剩余的n-1阶就有 f(n-1) 种跳法;
2.如果起始跳二阶的话,剩余的n-2阶就有 f(n-2) 种跳法;
所以f(n) = f(n-1) + f(n-2),实际结果即为斐波纳契数。
所以代码很容易就实现了:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

int Count(int n)
{
    if (n == 1)
        return 1;
    if (n == 2)
        return 2;
    else
        return Count(n - 1) + Count(n - 2);
}

int main()
{
    int n = 0;
    printf("请输入台阶数:\n");
    scanf("%d", &n);
    int count = Count(n);
    printf("%d阶台阶有%d种方法\n", n, count);
    system("pause");
    return 0;
}

那我们把这个问题升级一下:
如果某人可以一次性跳1~n阶,那他跳完n阶台阶有多少种跳法?
来我们分析一下:
1.如果起始跳一阶的话,剩余的n-1阶就有 f(n-1) 种跳法;
2.如果起始跳二阶的话,剩余的n-2阶就有 f(n-2) 种跳法;
3.如果起始跳三阶的话,剩余的n-3阶就有 f(n-3) 种跳法;

n.如果起始跳n阶的话,剩余的n-n阶就有 f(n-n) 种跳法;
已知一阶台阶时,跳法只有一种,f(1) = 1,二阶台阶时,跳法只有两种,所以f(2) = 1+1 = 2。
而f(2)=f(1)+ f(0); 所以f(0)=1;
第一次起跳有n种跳法,1,2,3,4,5…….n
所以f(n)=f(n-1)+f(n-2)+f(n-3)+…..+f(n-(n-1))+f(n-n)
即:
 f(n)=f(n-1)+f(n-2)+f(n-3)+…..+f(1))+f(0)
 f(n-1)=f(n-2)+f(n-3)+…..+f(1))+f(0)
 f(n-2)=f(n-3)+…..+f(1))+f(0)
等等….
以上式子可得:
f(n)=2*f(n-1)=2*2*f(n-2)=2*2*2*f(n-3)=….=2^(n-1)*f(1)=2^(n-1)

接下来代码就简单啦。

猜你喜欢

转载自blog.csdn.net/jsbgo201506010102/article/details/80144131
今日推荐