函数递归详解----跳台阶、斐波那契数列、汉诺塔问题

简介: 递归的思想:把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。递归中的递就是递推的意思,归就是回归的意思。

一、什么是递归? 


!!!递归就是函数自己调用自己!!!

# include <stdio.h>
int main ()
{
printf ( "hehe\n" );
main();                       //main 函数中⼜调⽤了 main 函数
return 0 ;
}
这里只是做一个简单的示例,这种写法是错误,会使程序陷入死递归导致 Stack overflow(栈溢出)。

递归的思想:

把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。

递归中的递就是递推的意思,归就是回归的意思。

二、递归的限制条件


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

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

三、递归的举例

递归求n的阶乘

int Fun(int n)
{
  if (n <= 1)
    return 1;
  else
    return n*Fun(n - 1);
}
int main()
{
  int n;
  printf("请输入所要求的阶乘:");
  scanf_s("%d", &n);
  int count = Fun(n);
  printf("该数字的阶乘为:%d", count);
  return 0;
}v


//画图演示
递推过程:
Fun(5) ------>   Fun(4)   ------> Fun(3)  ------>  Fun(2) ------>  Fun(1)  
=5*Fun(4)        =4*Fun(3)       =3*Fun(2)         =2*Fun(1)       =1  
回归过程:                                                                                                |||
Fun(5) <------   Fun(4)  <------   Fun(3)  <------ Fun(2)<------- Fun(2)=120                                                                                                                            =2

顺序打印整数的每一位

void Print(int n)
{
  if (n > 9)
  {
    Print(n / 10);
  }
  printf("%d ", n % 10);
}
int main()
{
  int m = 0;
  printf("请输入要打印的数字:");
  scanf_s("%d", &m);
  printf("最终结果为:");
  Print(m);
  return 0;
}


四、递归与迭代

       对于上述的Fun函数是可以产⽣正确的结果,但是在递归函数调⽤的过程中涉及⼀些运⾏时的开销

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

函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归 函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。 所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能 引起栈溢 出(stack over flow)的问题 。

顺序打印整数每一位在内存空间中的演示:



!!!递归层次不能太深,太深就会出现栈溢出的问题!!!

所以如果不想使⽤递归就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)

迭代实现n的阶乘:

int Fact(int n)
{
  int i = 0;
  int j = 1;
  for (i = 1; i <= n; i++)
  {
    j *= i;
  }
  return j;
}
int main()
{
  int n;
  printf("请输入所要求的阶乘:");
  scanf_s("%d", &n);
  int count = Fact(n);
  printf("该数字的阶乘为:%d", count);
  return 0;
}

事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰, 但是这些问题的迭代实现往往⽐递归实现效率更⾼。 当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。

斐波那契数列

斐波那契数列:1  1  2  3  5  8  13 21 24......

#include <stdio.h>
int count = 0;
int Fid(int n)
{
  if (n <= 0)
    return 0;
  else if (n == 1)
    return 1;
  else
    count++;
    return Fid(n - 1) + Fid(n - 2);
}
int main()
{
  int n = 0;
  printf("请输入要求第几个斐波那契数列中的数字:>");
  scanf_s("%d", &n);
  int ret = Fid(n);
  printf("该数字为:%d\n", ret);
  printf("需要计算的次数为: %d\n", count);
  return 0;
}


但是如果是要求第40个斐波那契数呢?


很明显在我们求第四十个斐波那契数的时候第三个斐波那契数将会被计算165580140次,这个数字太大了计算这个数所需要的内存空间也会很大不划算。

所以我们可以直接通过迭代来实现求解斐波那契数列:

#include <stdio.h>
int count = 0;
int Fun(int n)
{
  int a = 1;
  int b = 1;
  int c = 1;
  while (n > 2)    //当n>2时开始进行循环相加
  {
    c = a + b;
    a = b;
    b = c;
    count++;
      n--;
  }
  return c;      //当n<2时直接输出1
}
int main()
{
  int n = 0;
  printf("请输入要求第几个斐波那契数列中的数字:>");
  scanf_s("%d", &n);
  int num = Fun(n);
  printf("该数字为:%d\n", num);
  printf("所需要的次数为:%d",count);
  return 0;
}


很明显用迭代来解决该问题运算次数明显减少。


青蛙跳台阶问题:

一、问题描述

       有一只🐸,一次可以跳一个台阶,也可以跳2个台阶,如果有n个台阶,这只🐸有多少种跳法,跳上n个台阶?

二、问题分析     

 第一次跳有两种情况:


            1)当第一次跳一个台阶时: 剩余台阶数为n-1


            2)当第一次跳两个台阶时: 剩余台阶数为n-2


      所以当n>2时,跳法总数=第一次跳一个台阶之后的跳法数+第一次跳两个台阶之后的跳法数


      而对于n=2或者n=1时,显然易见此时的跳法总数固定,分别为2或1。

  tips:对于后者我们只需要进行简单的if判断即可。

int Jump(int n)
  {
  if (n == 2)
    return 2;
  else if (n == 1)
    return 1;
  else if (n > 2)
  {
  int count = Jump(n - 1) + Jump(n - 2);
  return count;
  }
  }
int main()
{
  int n;
  printf("青蛙要跳跃的台阶总数为:>");
  scanf_s("%d", &n);
  int count = Jump(n);
  printf("青蛙跳完所有台阶的方法总数为:%d", count);
}

汉诺塔问题:

一、问题描述

               将A柱上的盘子,借助于B柱,全部挪到C柱子上,挪动过程中A,B,C柱上的盘子要满足上面小下面大



二、问题分析

1)如果A只有一层:A(1)---->C(1)     1次移动



大概就是这么个意思,下面的两种就不再演示了

2)如果A有两层:A----->B      A----->C     B----->C     3次移动


3)如果A有三层:A----->C    A----->B    C----->B    A------>C    B----->A   B----->C  A----->C  7次移动


4)如果A有n层:2^n-1  次移动

具体代码实现如下:


#include <stdio.h>
void move(char pos1, char pos2)
{
  printf("%C ->%c ", pos1, pos2);
}
//n代表盘子的个数
//pos1:起始位置
//pos2:中转位置
//pos3:目的位置
void Hanoi(int n, char pos1, char pos2, char pos3)   //主运行的
{
  if (n == 1)      //如果初始位置只有一个方块就直接把它移到目的位置即可
  {
    move(pos1, pos3);
  }
  else
  {
    Hanoi(n - 1, pos1, pos3, pos2);
    move(pos1, pos3);
    Hanoi(n - 1, pos2, pos1, pos3);
  }
}
int main()
{
  Hanoi(1, 'A', 'B', 'C');
  printf("\n");
  Hanoi(2, 'A', 'B', 'C');
  printf("\n");
  Hanoi(3, 'A', 'B', 'C');
  printf("\n");
}

























相关文章
|
存储 关系型数据库 对象存储
|
存储 Oracle Unix
关于小机 | 计算机百年趣味史(上)第8篇
小机即小型机(minicomputer),从名字上我们可以知道是体积会较小的机器,不过体积也是针对大机(mainframe)来说是,如果光从绝对体积上讲,那显然又不对。所以,小机是对特定时代一群类似机器的统称。我们来看下小机的关键历史。其历史时间是与大型机并行的。
3301 0
关于小机 | 计算机百年趣味史(上)第8篇
|
7月前
|
机器学习/深度学习 存储 人工智能
AI 视频检测:重构食品质检体系,破解大规模生产品质难题
AI视频检测技术助力食品行业质检升级,通过实时感知、精准识别与数据驱动,实现从加工到成品的全流程智能管控,解决传统质检效率低、标准不统一等问题。
915 0
|
10月前
|
机器学习/深度学习 人工智能 大数据
35岁+大数据人必看:这6个证书,帮你把年龄变成职场「护城河」
35岁不是"职场黄昏",而是"经验红利期"。这些证书不是用来"装门面"的,而是帮你把十几年积累的行业认知,和最新的技术趋势结合起来的"加速器"。 考证的过程,本质上是逼自己跳出舒适区——你可能会重新学Python、研究机器学习模型、梳理数据治理流程,但这些"额外"的努力,都会变成你简历上的亮点、面试时的底气、谈薪资时的筹码。 记住:企业永远愿意为"能解决问题的人"买单。35岁的你,有经验、懂业务、还能学习新东西,这就是你最硬核的竞争力。 现在就开始挑一个证书,把焦虑变成行动力——毕竟,中年危机不可怕,可怕的是你还没开始准备。
|
人工智能 Cloud Native 安全
圆桌会议:聚焦AI时代机遇下操作系统产业的进化与重构 | 2024龙蜥大会主论坛
2024龙蜥大会主论坛聚焦AI时代的操作系统产业进化与重构。专家们围绕开源社区建设、商业化衍生、替代方案及AI应用等议题展开讨论。中国工程院陈纯院士强调开源社区的重要性,阿里云蒋江伟提出操作系统的兼容性和包容性,AMD潘晓明表示将加强国际合作,中兴通讯刘东则探讨了操作系统与AI的深度融合。会议一致认为,龙蜥操作系统应抓住AI发展机遇,构建安全可靠的生态体系,推动国产操作系统走向国际化。
303 3
|
Web App开发 前端开发 JavaScript
React性能优化指南:打造流畅的用户体验
React性能优化指南:打造流畅的用户体验
|
存储 消息中间件 缓存
本地缓存Caffeine系列(三)
本地缓存Caffeine系列(三)
|
弹性计算 分布式计算 运维
迟来的EMR Serverless Spark评测报告
本文是一篇关于阿里云EMR Serverless Spark产品评测的文章,作者分享了使用体验和理解。EMR Serverless Spark是阿里云提供的全托管、一站式的Spark数据计算平台,简化了大数据处理流程,让用户专注于数据分析。文章提到了产品的主要优势,如快速启动、弹性伸缩、高资源利用率和低成本。
589 8
|
JavaScript 前端开发 开发者
|
人工智能 算法 调度
优化问题之如何选择合适的优化求解器
优化问题之如何选择合适的优化求解器

热门文章

最新文章

下一篇
开通oss服务