首页 > 其他分享 >10 函数的应用:函数递归

10 函数的应用:函数递归

时间:2024-05-28 12:29:58浏览次数:17  
标签:10 return 函数 递归 int 阶乘 台阶 迭代

目录

一、什么是递归

(一)概念

(二)递归的思想

二、递归的条件

三、递归的举例

(一)分析与代码的实现

四、递归与迭代

(一)递归的缺陷

(二)迭代

(三)举例体现递归与迭代的区别

五、有意思的点

(一)递推的写法

(二)拓展学习

1、青蛙跳台问题

2、汉诺塔问题(儿童益智游戏)


一、什么是递归

(一)概念

        递归是一种解决问题的方法,在C语言中,递归就是函数自己调用自己

        一个简单的演示如下:

#include <stdio.h>

int main()
{
    printf("hehe\n");
    main();//main函数中⼜调⽤了main函数

    return 0;
}

        只作为简单的演示,不是为了解决问题,且代码最终也会陷入死递归,导致栈溢出(Stack overflow)

        原因:每一次函数调用,都要为这次函数调用分配内存空间,是从内存的栈区上分配的,如果无限的递归调用函数,就会将栈区空间填满(使用完),这时就出现了栈溢出的现象
 

(二)递归的思想

        通过函数自己调用自己可以发现,递归可以把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解,直到子问题不能再被拆分,递归就结束了;

        递归是少量的代码完成了大量复杂的运算;

        递归的思想就是把大事化小的过程;

        递归中的""是递推的意思,""就是回归的意思;

        理解如下:

二、递归的条件

        递归得有限制条件,不然很容易造成死递归等错误

        在书写递归代码时,有2个必要条件:

        ① 递归存在限制条件,当满足这个限制条件时,递归便不再继续

        ② 每次递归调用之后越来越接近这个限制条件

三、递归的举例

举例:求 n 的阶乘

一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1;

自然数 n 的阶乘写作 n!

题目:计算 n 的阶乘(不考虑溢出),n 的阶乘就是 1 ~ n 的数字累积相乘

(一)分析与代码的实现

        我们知道 n 的阶乘的公式:n! = n * (n - 1) !

举例:
5! = 5*4*3*2*1
4! = 4*3*2*1
所以:5! = 5*4!

        这样就把一个较大的问题,转化成一个与原问题相似,但规模较小的问题来求解;    

        当 n == 0 的时候,n 的阶乘为 1 ,其余的 n 的阶乘都是可以通过公式计算的,公式如下:

        实现代码如下:

int Fact(int n)
{
    if(n==0)
        return 1;
    else
        return n*Fact(n-1);
}

        测试:

#include <stdio.h>

int Fact(int n)
{
    if(n==0)
        return 1;
    else
        return n*Fact(n-1);
}

int main()
{
    int n = 0;
    printf("输入一个数求它的阶乘:");
    scanf("%d", &n);
    int ret = Fact(n);
    printf("\n阶乘为:%d\n", ret);

    return 0;
}

        结果为:

        画图演示:

四、递归与迭代

(一)递归的缺陷

        递归是一种很好的编程技巧,但是和很多技巧一样,也是可能被误用的,就像上面的举例一样,看到推导的公式,很容易就被写成递归的形式;

        虽然用递归思想来书写代码确实可以产生正确的结果,但是在递归函数调用的过程中涉及一些运行时的开销:

        在C语言中每一次函数调用,都需要为本次函数调用在内存的栈区,申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧

        函数不返回,函数对应的栈帧空间就一直占用,所以如果函数调用中存在递归调用的话,每一次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间;

        所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(Stack overflow)的问题

(二)迭代

        所以如果不想使用递归,就得想其他的办法,通常就是迭代的方式(通常就是循环的方式),如下演示:

        注:迭代是大类,循环是迭代,但迭代不一定是循环

int Fact(int n)
{
    int ret = 1;
    for(int i=1; i<=n; i++)
    {
        ret *= i;
    }

    return ret;
}

        事实上,我们看到的许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更加清晰,但是这些问题的迭代实现往往比递归实现效率更高;

        当一个问题非常复杂,难以使用迭代的方式实现时,此时递归实现的简洁性便可以补偿它所带来的运行时的开销

(三)举例体现递归与迭代的区别

例子:求第n个斐波那契数

一个斐波那契数等于前两个数之和,如:

1 1 2 3 5 8 13 21 34 55 ......

        公式如下:

        看到公式,很容易写成递归的形式,如下:

int Fib(int n)
{
    if(n<=2)
        return 1;
    else
        return Fib(n-1) + Fib(n-2);
}
#include <stdio.h>
int main()
{
    int n = 0;
    printf("输入一个数,计算对应的斐波那契数:");
    scanf("%d", &n);
    int ret = Fib(n);
    printf("\n第%d个斐波那契数为:%d\n", n, ret); 

    return 0;
}

        当我们输入 50 的时候,需要花很长的时间才能计算出结果,这是因为递归的层次太深了,冗余的计算会很多:

        画图分析可看出递归深度高达50次,且每次计算都是指数型增长,这些计算是非常冗余的,所以得用迭代的方式解决;

        迭代:我们知道斐波那契数的前2个数都1,然后前2个数相加就是第3个数,那么我们从前往后,从小到大计算就行了,代码如下:

int Fib(int n)
{
    int a = 1;
    int b = 1;
    int c = 1;
    while(n>2)
    {
        c = a+b;
        a = b;
        b = c;
        n--;
    }

    return c;
}

        使用迭代的方式来书写代码,效率就高出很多

五、有意思的点

(一)递推的写法

        三要素:

        ① 变成最简单问题的限制条件

        ② 变得更简单的语句(调用自己)

        ③ 最简单的一步的语句

        递推的书写过程中,三要素都齐全但没有及时回归,还是会造成栈溢出(Stack overflow)

(二)拓展学习

1、青蛙跳台问题

        青蛙可以跳一个台阶or两个台阶,n个台阶有几种跳法的理解:
        1个跳台:1种
        2个跳台:2种——跳2次1阶为一种,跳1次2阶为一种
        n个台阶:(n-1)个台阶跳上n个台阶时是一种跳法,(n-2)个台阶跳上n个台阶时是另一种跳法,所以n个台阶的跳法 = (n-1)个台阶跳法 + (n-2)个台阶跳法 :
        fib(n) = fib(n-1) + fib(n-2)——斐波那契数列

2、汉诺塔问题(儿童益智游戏)

        描述:借助于B,将A上的盘子,挪到C上,挪的过程中,盘子一定是上小下大

        思路:

        n-1个盘子到b,最大的n移到c;
        n-2个盘子到a,第二大的n-1移到c;
        如此反复......
        简单来说:除了最下面的盘子,其他的-1个盘子动来动去,而最下面的盘子到C


        

        以上博客仅供分析,若有错误,请多多指正,共同进步

标签:10,return,函数,递归,int,阶乘,台阶,迭代
From: https://blog.csdn.net/m0_66277256/article/details/139249982

相关文章

  • c++函数
    哈喽大家好,我是@菜就多练,输不起,就别玩,今天我来和大家分享函数函数就是在主函数上方的位置写,但也可以在下面写,常见函数类型有intdoubleboolstringchar.......不同函数类型它们的用法也不同,有判断的(bool),有计算的(intlonglong double),还有字符串的(charstring)等等等等那......
  • 2、补0函数
    补0函数1、FORMAT函数SELECTFORMAT(你的数字列,'0000000000');--这里的0的数量应该与你需要的位数相对应2、RIGHT和REPLICATE函数SELECTRIGHT(REPLICATE('0',你想要的数字位数)+CAST(你的数字列ASVARCHAR(MAX)),你想要的数字位数);3、例如,如果你有一个数字1,并且......
  • AP5101C高压线性LED恒流驱动芯片 6-100V 2A LED灯电源驱动
    产品描述AP5101C是一款高压线性LED恒流芯片,简单、内置功率管,适用于6-100V输入的高精度降压LED恒流驱动芯片。电流2.0A。AP5101C可实现内置MOS做2.0A,外置MOS可做3.0A的。AP5101C内置温度保护功能,温度保护点为130度,温度达到130度时,输出电流慢慢减小,达到保护芯片电......
  • 1、时间函数-DATEPART
    时间函数-DATEPART1、语法selectdatepart(datepart,date)datepart:所需要的输出的时间格式,如year、month、day、hour等。date:所要查询的时间字段(cloumn)。2、案例获取年份SELECTDATEPART(year,GETDATE())ASCurrentYear;获取月份SELECTDATEPART(month,GETDATE(......
  • (10-3)文件操作:包io
    10.3 包io在Go语言中,io是一个重要的标准库包,提供了一系列内置接口和方法,用于处理输入、输出流操作。包io中包含的常用接口如表10-1所示。表10-1 包io中包含的常用接口接口名称描述io.Reader代表可以读取数据的对象,定义了一个Read方法io.Writer代表可以写......
  • 打卡信奥刷题(22)用Scratch图形化工具信奥P1015 [NOIP1999 普及组] 回文数,写了一个好用
    P1015[NOIP1999普及组]回文数,用Scratch实现计算回文数,还写了一个比较好用的反序积木题目[NOIP1999普及组]回文数题目描述若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。例如:给定一个十进制数......
  • 力扣算法之1050. 合作过至少三次的演员和导演
    题解actor_id和director_id,类似一个坐标,只要出现三次或者三次以上就打印出来我的解SELECTactor_id,director_idFROMActorDirectorGROUPBYactor_id,director_idHAVINGCOUNT(1)>=3我的解注解同时分组,两个出现次数大于等于3的就是符合的,看了下,其他的思路和这个......
  • c++函数指针
     c/c++函数指针的用法【目录】基本定义c函数指针使用举例c++函数指针使用举例函数指针作为函数参数函数指针作为函数返回值函数指针数组typedef简化函数指针操作 c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);c++函数指针......
  • python中format() 函数的基础及项目中的应用
    format() 是Python中的一个字符串方法,用于格式化字符串。您可以使用大括号 {} 在字符串中插入占位符,然后在 format() 函数中提供要插入的值。下面是一些例子:基本用法:print('Hello,{}!'.format('world'))#输出:Hello,world! 在这个例子中,{} 是一个占位符,fo......
  • 彻底搞清楚vue3的defineExpose宏函数是如何暴露方法给父组件使用
    前言众所周知,当子组件使用setup后,父组件就不能像vue2那样直接就可以访问子组件内的属性和方法。这个时候就需要在子组件内使用defineExpose宏函数来指定想要暴露出去的属性和方法。这篇文章来讲讲defineExpose宏函数是如何暴露出去这些属性和方法给父组件使用。注:本文中使用的vue......