学习笔记---递归

41 篇文章 0 订阅
16 篇文章 0 订阅
本文深入探讨了递归算法的定义、类型和求解要点,包括直接递归和间接递归。通过数学归纳法举例说明了递归的应用,并提供了多个代码示例,如阶乘计算、递归规律解析及汉诺塔问题。文章强调了理解和掌握递归规律的重要性,同时也指出在特定情况下,迭代可能比递归更为高效,如解决汉诺塔和麦子问题。
摘要由CSDN通过智能技术生成

递归算法


释义:在调用一个函数的过程中又出现直接或间接的调用该函数本身,成为函数的递归(recursive)调用。


直接递归:


间接递归:



这两种递归调用,在形式上都是无终止的自身调用(实际解决问题时,可通过设置判断条件来跳出递归循环)



递归求解要点:

1.方法:

.列出递归方程/思路

.写出递归函数

.调用递归函数


2.举例:

数学归纳法

证明一个与自然数n有关的命题P(n),有如下步骤:

1.证明当n取第一个值a时命题成立(a对于一般数列取值为0或1)

2.假设当n=k(k>=a,k为自然数)时命题成立,证明当n=k+1时命题也成立


例:对于求n的阶乘



可分为以上两种思路:阶乘和递归


代码示例:

#include <stdio.h>
#include <stdlib.h>
/*这个程序用来测试通过递归计算n的阶乘*/

long fact(int);//因为结束时返回值可能会十分巨大,所以使用long作为返回值数据类型

int main()
{
    int n;
    printf("输入需要求阶乘的n:");
    scanf("%d",&n);
    printf("%d的阶乘是:%ld",n,fact(n));

    return 0;
}
long fact(int n)
{
    long f;
    if(n==1)//通过判断n的值决定是否递归,避免无限循环
        f=1;
    else
        f=n*fact(n-1);//调用自身,形成递归
    return f;
}
结果:


解析:

程序中用if语句来控制,只有在某一条件成立时才继续执行递归调用,否则就不再继续,这样,不会出现无终止的递归调用。


递归技巧


代码示例1:

#include <stdio.h>
#include <stdlib.h>
/*这个程序用来测试递归的技巧*/
/*输入一个整数m,要求输出其反序数以及正序数*/

void f1(int);//输出反序数
void f2(int);//输出正序数
int main()
{
    int m;
    printf("输入m:");
    scanf("%d",&m);
    f1(m);
    printf("\n");
    f2(m);
    printf("\n");
    return 0;
}
void f1(int m)
{
    if(m>0)
    {
        /*先输出,后递归*/
        printf("%d",m%10);
        f1(m/10);
    }
}
void f2(int m)
{
    if(m>0)
    {
        /*先递归,后输出*/
        f2(m/10);
        printf("%d",m%10);
    }
}

结果:


解析:

1.如上,改变递归的位置,得到的结果将是完全相反的。深刻理解这个例子的原理,即可理解递归的规律。


代码示例2:

#include <stdio.h>
#include <stdlib.h>
/*用递归实现二分查找*/

int r_search(int[],int,int,int);
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    int low=0,high=14,k;
    do{
        printf("输入希望查找的数,输入-1结束:\n");
        scanf("%d",&k);
        if(k==-1)
            break;
        int i=r_search(arr,low,high,k);
        if(i>=0)
            printf("在数组的第%d位找到该数,该数为:%d!\n",i,arr[i]);
        else
            printf("未找到该数!\n");
    }while(1);
    return 0;
}
int r_search(int arr[],int low,int high,int k)
{
    int i,mid;
    if(low>high)
        i=-1;//如果已经例遍了整个数组
    else
    {
        mid=(low+high)/2;
        if(arr[mid]==k)
            i=mid;//如果中间值等于期望值
        else if(arr[mid]>k)
            i=r_search(arr,low,mid-1,k);//如果中间值大于期望值
        else
            i=r_search(arr,mid+1,high,k);//如果中间值小于期望值
    }
    return i;
}
结果:


解析:

递归可以取代循环完成很多功能,而递归的代码往往更加简洁。

代码示例3:

#include <stdio.h>
#include <stdlib.h>
/*这个程序用递归思想来解决汉诺塔问题*/
/*
汉诺塔问题

有三根相邻的柱子,
标号为A,B,C,
A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,
要把所有盘子一个一个移动到柱子B上,
并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,
请问至少需要多少次移动?
*/
static long count=0;//计数变量
void move(int,char,char,char);//核心函数
int main()
{
    char a='A',b='B',c='C';//三根柱子
    printf("输入汉诺塔层数:");
    int n;
    scanf("%d",&n);
    move(n,a,b,c);
    printf("\n总共使用了%ld步",count);
    return 0;
}
/*注意!move函数中用到的所有a,b,c都是形参!在递归过程中,a代表的可能是字符A也可能是字符B、C!b和c也同理*/
/*如要理解这个函数,可通过单步调试,设置n为4以内的较小的数进行测试*/
void move(int n,char a, char b,char c)
{
    if(n==1)
    {
        count++;
        printf("%c-->%c\n",a,c);//如果现在的问题时把一个盘子从a移动到c,那么直接做就行了。不需要进一步分化问题
        return;
    }
    move(n-1,a,c,b);//要把n个盘子经过b从a移动到c,首先要把最底下最大的那个盘子之上的n-1个盘子经过c从a移动到b。
    count++;
    printf("%c-->%c\n",a,c);//现在a上只有一个盘子了,直接将他移动到c即可
    move(n-1,b,a,c);//现在情况是,a上没有盘子,且要把剩下的盘子从b移动到c。(类比b上没有盘子,要把盘子从a移动到c。可以进入下一次递归)
}
结果:


解析:

1.汉诺塔问题是递归的经典应用,通过这个问题也能更加深刻的理解递归。但递归并不是解决这个问题的最佳方案。

2.如果要输出每一步移动的轨迹,应当使用递归。但如果只要输出移动所需步数,则应当使用迭代!


以下是使用迭代计算汉诺塔、麦子问题的代码示例,与本篇主旨无关。


代码示例:

#include <stdio.h>
#include <stdlib.h>
/*
麦子问题

舍罕王打算奖赏国际象棋的发明人──宰相西萨·班·达依尔。
国王问他想要什么,他对国王说:“陛下,请您在这张棋盘的第1个小格里赏给我一粒麦子,
在第2个小格里给2粒,第3个小格给4粒,以后每一小格都比前一小格加一倍。
请您把这样摆满棋盘上所有64格的麦粒,都赏给您的仆人吧!”
国王觉得这个要求太容易满足了,就命令给他这些麦粒。
当人们把一袋一袋的麦子搬来开始计数时,国王才发现:
就是把全印度甚至全世界的麦粒全拿来,也满足不了那位宰相的要求。
请计算共需要多少麦子才能满足其要求。
*/
/*麦子问题和汉诺塔问题是相似的。用这个程序同样可以计算n层汉诺塔的移动总需步数*/
/*比如输入5,便是5层汉诺塔总共所需移动步数*/
int main()
{
    /*难点在于如何表达一个这么大的数,C语言中long long也不足以做到这一点,但unsigned long long 却刚刚好能做到*/
    unsigned long long totol=0,now=1;//这几乎是C语言能表达数字最大的基础数据类型了。
    int n;
    scanf("%d",&n);
    while(n--)
    {
        totol+=now;
        now*=2;
    }
    printf("%llu",totol);//输出的格式字符串也和一般情况下有所不同
    return 0;
}
结果:


解析:

1.如上,如果用递归来算64层汉诺塔或者64格棋盘的话,需要几天几夜才可能得到结果。而用迭代只需要1秒多一点。

2.unsigned long long 的输出格式字符串是%llu或%I64u(后者的I是大写的i,且后者原用于输出_int64型数据,如今也可用于输出unsigned long long),long long的输出格式字符串是%lld。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值