c语言的简易教法—— 函数递归

文章目录

  • 一、什么是递归?
    • 1.1递归的思想
    • 1.2递归的限制条件
  • 二、递归案例
    • 2.1 案例1:求n的阶层
      • 2.1.1分析
      • 2.1.2 递归函数(Fact)的代码实现
      • 2.1.3 测试:main函数实现
      • 2.1.4 运行结果和画图推演
      • 2.1.5 扩展:迭代方法求解n的阶乘
    • 2.2 案例2:顺序打印⼀个整数的每⼀位
      • 2.2.1分析
      • 2.2.2打印数(print)的每一位代码实现
      • 2.2.3 测试:print函数实现
      • 2.2.4 运行结果和画图推演
    • 2.3 案例3:求第n个斐波那契数
      • 2.3.1分析
      • 2.3.2求第n个斐波那契数(fib) 的代码实现
      • 2.3.3 测试:fib函数实现
      • 2.2.4 运行结果和画图推演
      • 2.1.5 扩展:迭代方法求解斐波那契数
    • 2.4 案例4:递归实现n的k次方
      • 2.4.1分析
      • 2.4.2 mypow函数实现
      • 2.4.3 测试:主函数实现mypow函数
      • 2.4.4 运行结果
  • 总结


一、什么是递归?

在代码运行中,有时候我们碰到冗长的代码无法进行下笔进行编码,这时候我们将会学习到应用函数递归进行运算。那么什么是递归呢?

1.1递归的思想

什么是函数递归?函数递归就是把大事化小,把小事在进行化小,直到解决问题。函数递归主要分为两个过程一个叫递推,一个叫回归。

递推主要是将大事简化成一个个小事逐渐往下进行,回归就是把最小的事情解决然后逐渐传递给上一个值进行回归计算值,直到返回最初需要解决的问题。

1.2递归的限制条件

递归存在两种限制条件 :

1.递归存在限制条件, 递推不能无限一直递推下去,即递推存在限制条件:什么时候进入递归,递归什么时候结束,递归存在进入递归的条件和递归的结束条件。只有满足这个限制条件,递归将不会继续递归下去。

为什么函数递归不能无限递归的原因

在这里插入图片描述

在这里我简单写了一个递归函数,内存中分为几个不同的区域,在函数运行中产生的局部变量都会存放在内存中的栈区,接着我们看到函数,首先进入主函数main,然后我们会创建一个栈帧空间存储main函数中的变量,然后代码继续运行下去我们调用test函数,调用test函数会开辟一个栈帧空间,在test函数中再次进行调用test函数就会出现如上图一样的情况,内存中的栈区也是有一定空间的,每次调用函数都会额外开辟一份空间,如果一直无限调用下去栈区将会溢出。

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

因为每次递归,相当于都是一次新的函数调用,而每次函数调用系统必须给该函数划分栈帧空间,内部的递归函数没有退出,上层的递归就不能退出,栈帧就会累积许多块,如果累积超过栈的总大小,就会栈溢出。所以函数递归每次都需要逐渐的接近这个函数递归的停止条件,否则他就会无限一直递归下去。


提示:以下是本篇文章代码部分,下面案例可供参考

二、递归案例

2.1 案例1:求n的阶层

⼀个正整数的阶乘(factorial)是所有⼩于及等于该数的正整数的积,并且0的阶乘为1。
⾃然数n的阶乘写作n!。

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

2.1.1分析

在这里插入图片描述

上面分析图解中,我们就要有递归的思想:把一个较大的事情转化为一个与原问题相似,但规模较小的问题进行求解。

当n==0的时候,n的阶乘是1,其余的阶乘都是可以用如上图一样的规律可以推出来的。

递归公式

2.1.2 递归函数(Fact)的代码实现

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

2.1.3 测试:main函数实现

#include <stdio.h>
int Fact(int n)  
{if(n==0)  return 1;  else  return n*Fact(n-1);  
}
int main()  
{int n = 0;  scanf("%d", &n);  int ret = Fact(n);  printf("%d\n", ret);  return 0;  
}

2.1.4 运行结果和画图推演

注意此处不考虑n太大,n太大会存在溢出的情况,因为这是一个整型变量,存储的最大值65530
在这里插入图片描述

在这里插入图片描述

2.1.5 扩展:迭代方法求解n的阶乘

#include<stdio.h>int Fact(int n)
{int i = 0;int sum = 1;for (i = 1; i <= n; i++){sum = sum * i;}return sum;
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fact(n);>   printf("%d\n", ret);   return 0;   
}

2.2 案例2:顺序打印⼀个整数的每⼀位

题目:输⼊⼀个整数m,打印这个按照顺序打印整数的每⼀位。
例如:

输入1234,打印1 2 3 4
输入4578,打印4 5 7 8

2.2.1分析

首先看到打印的第一步,我们想到的是怎么求出该数的每一位:
如果该数是一位数,那我们直接打印这一位即可。
如果该数超过一位数,那我们就得一个个求出该数的每一位。

假如我们要打印1234的每一位,我们得先求出他的每一位

1234 % 10 = 4,在这里我们得到了个位上的数4。 1234 / 10 = 123;
123 % 10 = 3, 在这里我们得到了十位上的数3。 123 / 10 = 12;
12 % 10 = 2,在这里我们得到了百位上的数2。 12 / 10 =1;
1 % 10 = 1 ,此时一位数我们直接打印即可。1 / 10 =0;由这里可以判断结束递归的条件是该数大于9.
但是这里发现最先得到的是个位上的数,因此我们这里弄出一个函数print
print(1234) = print(123) + printf(4);= print(1234/10) + printf(1234%10)
print(123) = print(12) + printf(3) + printf(4) ; = print(123/10) + printf(123%10)
print(12) = printf(1) + printf(2)+ printf(3) + printf(4) ;

2.2.2打印数(print)的每一位代码实现

void Print(int n) voidPrint(国际)       
{if(n>9) 如果(n>9{Print(n/10); 打印(n/10;       }printf("%d ", n%10); printf(“%d ”,n%10;       
}

2.2.3 测试:print函数实现

#include<stdio.h>
void Print(int n)  
{if(n>9)  {Print(n/10);  }printf("%d ", n%10);  
}
int main()
{int m = 0;scanf("%d", &m);Print(m);return 0;
}

2.2.4 运行结果和画图推演

在这里插入图片描述
在这里插入图片描述

2.3 案例3:求第n个斐波那契数

题目:求第n个斐波那契数;

输入: 5 输出:5
输入:10 输出:55

2.3.1分析

有不知道什么叫斐波那契数列的同学可以百度搜索详细了解一下,在这里小编就简单给大家介绍一下什么叫斐波那契数列。斐波那契数第n个数等于前两个数之和的相加,因此斐波那契数列第一第二个数都为1,以此推导剩下的斐波那契数:
1 1 2 3 5 8 13 21 34 55 。。。。
因此我们可以推导出求第n个斐波那契数的公式为:
在这里插入图片描述

2.3.2求第n个斐波那契数(fib) 的代码实现

int fib(int n)
{if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}

2.3.3 测试:fib函数实现

#include<stdio.h>
int fib(int n)
{if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}
int main()
{int m = 0;scanf("%d", &m);int ret = fib(m);printf("%d ",ret);return 0;
}

2.2.4 运行结果和画图推演

在这里插入图片描述

在这里插入图片描述

2.1.5 扩展:迭代方法求解斐波那契数

在上面细心的人就会发现我们求解第50个斐波那契数,电脑突然间疯狂的转起来,但是始终等了很久也没有得到第50个斐波那契数的值,这是为什么呢?

在这里插入图片描述

由上面的图我们可以看出,当我们在进行递归运算的时候,他会重复计算很多的重复值,例如我们上面再递归求fib(49)需要求fib(48)和fib(47);而我们求fib(48)又会求一次fib(47)和fib(46),这时随着递归的层次原来越深,我们会发现我们会重复计算很多次重复的值,接下来我们计算一下算fib(3)一共计算了多少次。

#include<stdio.h>
int count = 0;
int fib(int n)
{if (n == 3)count++;if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}
int main()
{int m = 0;scanf("%d", &m);int ret = fib(m);printf("%d\n",ret);printf("count = %d",count);return 0;
}

在这里插入图片描述

这时我们可以发现fib(3)重复计算了39088619次,这大大加大了计算机运行的难度,因此求解斐波那契数的时候递归求解是一个错误的选择。因此我们可以直接采用迭代的求法。

#include<stdio.h>
int fib(int n)
{int a = 1; //开始时第一个斐波那契数int b = 1; //开始时第二个斐波那契数int c = 1; //返回求解的斐波那契数,因为如果n<=2返回1所以c的初始值为1while (n > 2){c = a + b;a = b; b = c; n--; }return c; 
}
int main() 
{int m = 0; scanf("%d", &m); int ret = fib(m); printf("%d\n",ret); return 0; 
}

2.4 案例4:递归实现n的k次方

模拟实现pow函数实现求解n的k次方

输入:2 3 输出:8
输入:3 3 输出:27

2.4.1分析

当k的值为0的时候,返回1;
当k的值为1的时候,返回n;
当k的值大于1的时候,返回n*mypow(k-1);
综上:
在这里插入图片描述

2.4.2 mypow函数实现

int mypower(int k, int n)
{if (n == 0)return 1;else if(n >= 1)return k * mypower(k, n - 1);
}

2.4.3 测试:主函数实现mypow函数

int mypower(int k, int n)
{if (n == 0)return 1;else if(n >= 1)return k * mypower(k, n - 1);
}
int main()
{int k = 0;int n = 0;scanf("%d%d", &k, &n);int ret = mypower(k, n); printf("%d ", ret); return 0;  }

2.4.4 运行结果

在这里插入图片描述


总结

通过上面案例我们初步了解了函数递归的思想 :把大事化小的特点。明白函数递归的充要条件是1,首先要有限制条件,即进入递归的条件和结束递归的条件。2,在函数递归的时候我们要逐渐的接近这个递归条件。
当然函数递归的学习远不如于此,需要大家在不断的实践练习中不断了解函数递归的妙用。这是小编对于函数递归的理解如果有读者有看不懂的地方或者更好的建议欢迎评论下方留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/44038.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为如何做成数字化转型?

目录 企业数字化转型是什么&#xff1f; 华为如何定义数字化转型&#xff1f; 为什么做数字化转型&#xff1f; 怎么做数字化转型&#xff1f; 华为IPD的最佳实践之“金蝶云” 企业数字化转型是什么&#xff1f; 先看一下案例&#xff0c;华为经历了多次战略转型&#xf…

前端工程化:Webpack配置全攻略

前端工程化&#xff1a;Webpack配置全攻略 前端小伙伴们&#xff0c;今天我们来聊聊那个让人又爱又恨的 Webpack。没错&#xff0c;就是那个配置起来让你想砸键盘&#xff0c;但又离不开它的构建工具。别担心&#xff0c;跟着我来&#xff0c;保证让你从 Webpack 小白变成配置…

Windows 虚拟机服务器项目部署

目录 一、部署JDK下载JDK安装JDK1.双击 jdk.exe 安装程序2.点击【下一步】3.默认安装位置&#xff0c;点击【下一步】4.等待提取安装程序5.默认安装位置&#xff0c;点击【下一步】6.等待安装7.安装成功&#xff0c;点击【关闭】 二、部署TomcatTomcat主要特点包括&#xff1a;…

感应触摸芯片集成为MCU,深度应用触控按键技术的VR眼镜

VR&#xff08;Virtual Reality&#xff09;即虚拟现实&#xff0c;简称VR&#xff0c;其具体内涵是综合利用计算机图形系统和各种现实及控制等接口设备&#xff0c;在计算机上生成的、可交互的三维环境中提供沉浸感觉的技术。它的工作原理是将左右眼图像交互显示在屏幕上的方式…

技术速递|宣布为 .NET 升级助手提供第三方 API 和包映射支持

作者&#xff1a;Marco Goertz 排版&#xff1a;Alan Wang .NET 升级助手是一个 Visual Studio 扩展和命令行工具&#xff0c;可帮助您将应用从之前的 .NET 和 .NET Framework 升级到最新版本的 .NET。正如我们在之前的文章中所描述的那样&#xff0c;它为升级 Microsoft 库和框…

【C语言】 —— 预处理详解(下)

【C语言】 —— 预处理详解&#xff08;下&#xff09; 前言七、# 和 \##7.1 # 运算符7.2 ## 运算符 八、命名约定九、# u n d e f undef undef十、命令行定义十一、条件编译11.1、单分支的条件编译11.2、多分支的条件编译11.3、判断是否被定义11.4、嵌套指令 十二、头文件的包…

Day1每日编程题日记:数字统计、两个数组的交集、点击消除

前言&#xff1a;该篇用于记录自看。曾回看昨天的做题代码&#xff0c;竟然会觉得陌生&#xff0c;这竟然是我写的&#xff0c;细细读了一下&#xff0c;原来我当时是这么想的。因此我觉得记代码没有实际用处&#xff0c;重点是领悟了思想&#xff0c;这样子代码就在心中&#…

HashMap----源码解读

源码分析&#xff1a; public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable 在类的开头声明了几个常量&#xff0c;以下是较为重要的&#xff1a; /*** 定义初始容量大小为16*/ static final int DEFAULT_I…

探索【Python面向对象】编程:新时代的高级编程范式详解

目录 1. 面向对象编程概念&#xff08;OOP&#xff09; 1.1 什么是类和对象&#xff1f; 1.2 类的定义 1.3 类和对象的关系 1.4 小李的理解 2. 抽象 2.1 抽象的概念 2.2 抽象类和方法 2.3 小李的理解 3. 类和实例 3.1 类的定义和实例化 3.2 类的属性和方法 3.3 小…

如何使用Python在企业微信中发送测试结果?操作看这里!

在日常的自动化测试工作中&#xff0c;一般会需要把测试结果同步到工作群里&#xff0c;方便信息同步。那么我们今天就使用企业微信和Pythonrequests库来演示一下具体如何操作吧&#xff01; 01 准备 开始之前&#xff0c;我们应该确保已经安装了python环境&#xff0c;并且要…

DNS知识点

📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 ​ 目录 一、DNS概念 二 hosts 文件 三 DNS优缺点 三 客户端域名解析顺序(优先级)…

8.9分王者“水刊”!1区IEEE-Trans,国人主编坐镇!发文量2倍增长,扩刊趋势明显!

关注GZH【欧亚科睿学术】&#xff0c;第一时间了解最新期刊动态&#xff01; 本期&#xff0c;小编给大家推荐的是一本IEEE旗下王者“水刊”。该期刊目前处于扩刊状态&#xff0c;接收跨学科领域&#xff0c;领域认可度高&#xff0c;还可选择非OA模式无需版面费&#xff0c;是…

PPTP、L2TP、IPSec、IPS 有什么区别?

随着互联网的发展&#xff0c;保护网络通信的安全越来越重要。PPTP、L2TP、IPSec、IPS是常见的网络安全协议和技术&#xff0c;在保护网络通信安全方面发挥着不同的作用和特点。下面介绍PPTP、L2TP、IPSec、IPS之间的区别。 点对点隧道协议&#xff08;PPTP&#xff09;是一种用…

HTTP协议分析/burp/goby/xray

一、HTTP简介 HTTP(超文本传输协议)是今天所有web应用程序使用的通信协议。最初&#xff0c;HTTP只是一个为获取基于文本的静态资源而开发的简单协议&#xff0c;后来人们以名种形式扩展和利用它.使其能够支持如今常见的复杂分布式应用程序。HTTP使用一种用于消息的模型:客户端…

javaweb中的请求与响应--基于postman工具的应用(附带postman的详细安装步骤)

一、前言 后端的第一天感觉难度就上来了&#xff0c;可能是基础太过薄弱了吧。目前看视频已经有点跟不上了&#xff0c;果然15天想要拿下还是太勉强了点。30天还差不多。不知道读者们有没有好好的去学这方面的知识&#xff0c;没有什么是学不会的&#xff0c;关键是坚持。 Po…

几个小创新模型,KAN组合网络(LSTM、GRU、Transformer)回归预测,python预测全家桶再更新!...

截止到本期&#xff0c;一共发了9篇关于机器学习预测全家桶Python代码的文章。参考往期文章如下&#xff1a; 1.终于来了&#xff01;python机器学习预测全家桶 2.机器学习预测全家桶-Python&#xff0c;一次性搞定多/单特征输入&#xff0c;多/单步预测&#xff01;最强模板&a…

萝卜快跑的狠活

萝卜快跑作为百度旗下的自动驾驶出行服务平台&#xff0c;在科技应用上展现了多项领先的技术。以下是萝卜快跑采用的一些主要科技“狠活”&#xff1a; 自动驾驶技术&#xff1a; 萝卜快跑主要使用了百度Apollo的L4级自动驾驶技术&#xff0c;该技术能够应对海量的城市道路场景…

C++:重定义

派生类和基类的同名成员问题 派生类中再实现一个基类中的方法会怎样 (1)代码实验&#xff1a;派生类和基类中各自实现一个内容不同但函数原型完全相同的方法&#xff0c;会怎么样 (2)结论&#xff1a;基类对象调用的是基类的方法&#xff0c;派生类对象调用执行的是派生类中重…

进程调度篇

在操作系统的广阔领域中&#xff0c;进程调度是其中一个至关重要的环节。它如同操作系统的“交通警察”&#xff0c;负责在多个等待CPU执行的进程间进行高效、公平的分配。本文将带您了解进程调度的基本概念、重要性、常用算法…… 1. 进程调度的基本概念 1.1 进程调度的定义 …

【FreeRTOS】freeRTOS的Tmr Svc任务优先级配置

1、Tmr Svc是个FreeRTOS的软件定时器任务&#xff0c;他可以收集各任务的状态 2、他的优先级可以通过宏 configTIMER_TASK_PRIORITY 来配置&#xff0c;默认是2 3、修改为31后&#xff0c;程序总是启动不了&#xff0c; 4、后面才发现原来FreeRTOS的默认最大优先级号配置的是…