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

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

一、什么是递归? 


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

# 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");
}

























相关文章
|
Java Maven 开发者
Java中的注解处理器详解
Java中的注解处理器详解
|
8月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
202 13
|
10月前
|
Web App开发 前端开发 JavaScript
React性能优化指南:打造流畅的用户体验
React性能优化指南:打造流畅的用户体验
|
机器学习/深度学习 数据采集 数据可视化
深度学习实践:构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行分类
本文详细介绍如何使用PyTorch构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行图像分类。从数据预处理、模型定义到训练过程及结果可视化,文章全面展示了深度学习项目的全流程。通过实际操作,读者可以深入了解CNN在图像分类任务中的应用,并掌握PyTorch的基本使用方法。希望本文为您的深度学习项目提供有价值的参考与启示。
|
弹性计算 分布式计算 运维
迟来的EMR Serverless Spark评测报告
本文是一篇关于阿里云EMR Serverless Spark产品评测的文章,作者分享了使用体验和理解。EMR Serverless Spark是阿里云提供的全托管、一站式的Spark数据计算平台,简化了大数据处理流程,让用户专注于数据分析。文章提到了产品的主要优势,如快速启动、弹性伸缩、高资源利用率和低成本。
462 8
|
存储 监控 Java
使用Elasticsearch实现全文搜索的最佳实践
使用Elasticsearch实现全文搜索的最佳实践
java编写枚举校验类
java编写枚举校验类
206 0
|
机器学习/深度学习 算法框架/工具 TensorFlow
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(五)(4)
Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(五)
194 0
|
SQL 关系型数据库 MySQL
MySQL保存或更新 saveOrUpdate
MySQL保存或更新 saveOrUpdate
183 0
|
芯片
ADC模数转换器(内含:1.实物图+2.ADC简介+3.ADC框图+4.ADC基本结构图+5.输入通道+6.转换模式+7.触发控制+8.数据对齐+9.硬件电路)
ADC模数转换器(内含:1.实物图+2.ADC简介+3.ADC框图+4.ADC基本结构图+5.输入通道+6.转换模式+7.触发控制+8.数据对齐+9.硬件电路)
942 0
ADC模数转换器(内含:1.实物图+2.ADC简介+3.ADC框图+4.ADC基本结构图+5.输入通道+6.转换模式+7.触发控制+8.数据对齐+9.硬件电路)