C语言函数—递归理解和练习

news2025/1/18 12:44:31

练习:

编写函数不允许创建临时变量,求字符串的长度。

我们看到这道题,第一个想到的是不是strlen

int main()
{
    char[] = "bit";
    //['b']['i']['t']['\0']
    //里面一共4个字符(包括结尾的、0)但是我们的strlen函数并不会计算\0的长度,所以结果应该为10
    printf("%d\n",strlen(arr));
    return 0;
}

那我们写求字符串长度的函数,那不就是模拟实现strlen函数吗

我们把函数命名为my_strlen

我们再来思考一下,strlen传参穿的是什么呢?

是字符串首元素的地址

返回参数应该为字符串的长度,也就是整型变量

//多以我们的函数输入参数应该这么写
int my_strlen(char* str)
{
    
}

里面怎么实现呢?

['b']['i']['t']['\0']//数组内容
//我们传递的参数一定是['b'],的地址,那strlen怎么知道字符串长度呢
//没错,就是去寻找['\0']

首先来看,计数怎么实现呢?

int my_strlen(char* str)
{
    int count = 0;
    while(*str != '\0')
    {
        count++;
        str++;//指针的偏移
    }
    return count;
}

我们看一下运行结果

image-20240315203248388

成功打印出了字符串的长度

很简单的实现方式,但是题目上要求是什么,不允许创建临时变量

我们在程序运行的过程中,创建了count作为临时变量,很明显是不对的

那我们该怎么做呢?

我们应该思考递归的想法

int my_strlen(char* str)
{
    
}

我们重新来,怎么才能使用递归的思想呢?

什么是递归?

程序调用自身的编程技巧称为递归( recursion)。

递归做为一种算法在程序设计语言中广泛应用。

一个过程或函数在其定义或说明中有直接或间接 调用自身的 一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的主要思考方式在于:把大事化小


好,我们又复习了一下

大家请思考一下,我们是不是很容易就把第一个字符取出来(只判断第一个字符是不是\0)

my_strlen(“bit”);

1+my_strlen(“it”);

1+1+my_strlen(“t”);

1+1+1my_strlen(“”);

1+1+1+0

把大事化小

我们来看代码

int my_strlen(char* str)
{
    if (*str != '\0')//判断第一个字符是不是结束
    {
        return 1 + my_strlen(str+1);//如果第一个字符不是\0的话就即为1+剩下字符的长度
    }
    else
        return 0}

我们看看结果

image-20240315205304167

哈哈,是不是真的很神奇

还有一个问题

return 1 + my_strlen(str+1);

写为

return 1 + my_strlen(str++);

行不行?

image-20240315205332558

为什么不行,因为str++是后置++

后置++是干什么的,先使用再++,也就是说传进去还是原来的值,留下来的是加1后的值,是不是就和我们想要的不一样了?

改为前置++就可以了

image-20240315205514231


第二个练习:

求n的阶乘。(不考虑溢出)

我们还是先写主函数

int main()
{
    int n;
    scanf("%d", &n);
    printf("%d\n", factorial(n));
    return 0;
}

那递归函数该怎么写呢?

//我们可以这么想
//每一次相乘都乘自己减去一,直到为0的时候,返回1

那函数是不是能这么写

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

很简单,我们来看一下结果

image-20240315211500608


练习三:斐波那契数列

什么是斐波那契数列?

image-20240315211941774

第一个数字为1,后面的数字都是前两个数字的和

那用递归的思想该怎么解决呢?

每一次迭代,都是自己加上前一个数字
return 自己上一个数字 + 上一个数字的上一个数字

当然,递归终结条件就是到2以下的时候,当自己小于等于2,直接返回1就行

是不是还是很不好理解,那我再举一个更详细的例子

我现在要求第五个斐波那契数列的数字
    
1.我们需要求第四个数字加第三个数字

2.我们需要求出   第二个数字+第三个数字(第四个数字)   +   第一个数字+第二个数字(第三个数字)
    //注意了,这里出现了循环终结条件,斐波那契数列的第一个和第二个数字都为1,那么可以直接带入
3.我们需要求出   1 +  第一个数字+第二个数字(第三个数字)    +    1 +1 
    
4.那么递归都来到了递归终结条件,开始整个程序出栈计算结果,结果就是 1+1+1+1+1 = 5

所以我们可以推导出来这个函数

int Fib(int n)
{
    if(n <= 2)
        return 1;
    else
        return Fib(n-1)+Fib(n-2);
}

看一下计算结果

image-20240315214217106

这里还有一个问题,如果我要求第五十个数字呢?

运行时你就会发现,程序一直在计算,是程序偷懒了算不出来结果吗

我们如果看一下计算过程就会发现,整个程序的复杂度是2的n次方,也就是说我如果要计算第五十个数字,需要计算1125899906842624次

效率太低 —— 重复大量的计算

那该如何避免重复的计算呢,很简单其实,我们从前往后算就行,让前两个数相加等于第三个数,一直到第n个数为止

#include <stdio.h>
int Fib(int n)
{
    int i = 0;
    int a = 1;
    int b = 1;
    int c = 1;//当不进入数列时直接返回C,所以把C初始化为1
    while(n > 2)
    {
        c = a + b;
        a = b;
        b = c;
        n--;
    }
    return c;
}

int main()
{
    int n;
    scanf("%d", &n);
    printf("%d\n", Fib(n));
    return 0;
} 

很容易理解,我们可以测试一下

image-20240315215225519

这次的时间复杂度O(n)就很低

直接计算第五十个数也是会在1秒内算出答案,但是因为整型表示范围有限溢出了,这里就不放图了


我们的代码可以用递归写,也可以用非递归来写

有时候当我们使用递归时,可能会导致栈溢出,大量的重复计算导致的效率低下

我们总是要想出一个方法要用非递归的方法来写

这个时候再难也要去找i到这样一种非递归的方法


最后一个递归问题,也是最经典的一个迭代问题

汉诺塔

经典的递归的问题

image-20240315215938345

image-20240315220000136

我总结一下

  • 一共有三根柱子,第一根柱子有从大到小的圆盘
  • 我们需要把圆盘移到第三根柱子上
  • 每次只能挪动一个
  • 小圆盘只能在大圆盘的上面

现在有n个圆盘那我们该如何通过程序算出来最少需要多少次移动才能成功呢?

大家可以思考一下

会尽快快更新笔记讲解这个问题的?


感谢大家的阅读!!!

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

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

相关文章

谷歌网络营销方案有几种?​

谷歌作为海外的头部工具&#xff0c;本身其实就有多种工具可以供你使用&#xff0c;在这里说说谷歌那些工具 Google My Business&#xff0c;对于小企业或者本地服务来说&#xff0c;把自己的业务信息优化并完善在Google My Business上是个不错的选择。这样当人们在附近搜索相…

可视化场景(4):财务场景,公司经营的晴雨表。

在财务场景中&#xff0c;可视化大屏具有以下8个应用价值&#xff1a; 销售和收入分析 可视化大屏可以展示销售额、收入来源、销售渠道等数据&#xff0c;帮助财务团队分析销售趋势和收入结构&#xff0c;发现潜在的增长机会和问题。 成本和费用管理 可视化大屏可以显示成本…

蓝桥杯2022年第十三届省赛真题-裁纸刀

443 对于m行n列 次数 4 m - 1 (n-1)*m 其中4是裁掉边缘&#xff1b;行需要裁m-1次&#xff1b;每个小长条需要裁n-1次&#xff0c;一共有m个小长条

MFMailComposeViewController 发送邮件

通过 MFMailComposeViewController 发送邮件,需预先登录邮箱账号的情况下; 具体实现与配置参数请参考如下: 首先,引入 MFMailComposeViewController 库 #import <MessageUI/MessageUI.h> 其次,实现相关 api 方法 if ([MFMailComposeViewController canSendMail]) {MFM…

通过spring boot/redis/aspect 防止表单重复提交【防抖】

一、啥是防抖 所谓防抖&#xff0c;一是防用户手抖&#xff0c;二是防网络抖动。在Web系统中&#xff0c;表单提交是一个非常常见的功能&#xff0c;如果不加控制&#xff0c;容易因为用户的误操作或网络延迟导致同一请求被发送多次&#xff0c;进而生成重复的数据记录。要针…

解决ubuntu 22.04新内核6.5.0-15无法编译NVIDIA显卡驱动

这里的新内核应该包括6.5.*系列的 文章目录 遇到的问题&#xff1a; 遇到的问题&#xff1a; 今天我在安装NVIDIA显卡驱动发现了一个问题&#xff0c;主要日志如下所示&#xff1a; make[3]: *** [scripts/Makefile.build:251: /tmp/selfgz1310041/NVIDIA-Linux-x86_64-550.5…

综合利用Cisco Packet Tracer模拟器配置园区网

1. 内容 1.在课室交换机中创建各个课室的VLAN&#xff0c;并将1-20端口平均分配给各个课室。 2.使用课室交换机的每个端口只能接入一台计算机&#xff0c;发现违规就丢弃未定义地址的包。3.网络内部使用DHCP分配各课室的IP地址&#xff0c;在课室交换机按照第一题划分的VLAN地…

蜡烛图K线图采用PictureBox控件绘制是实现量化交易的第一步非python量化

用vb6.0开发的量化交易软件 VB6量化交易软件的演示视频演示如上 股票软件中的蜡烛图是非常重要的一个东西&#xff0c;这里用VB6.0自带的Picture1控件的Line方法就可以实现绘制。 关于PictureBox 中的line 用法 msdn 上的说明为如下所示 object.Line [Step] …

C#使用迭代算法计算斐波那契数列通项

目录 1.斐波纳契数列 2.迭代一次产生1个新的通项 3.迭代一次产生2个新的通项 1.斐波纳契数列 斐波纳契数列的定义是&#xff0c;它的第一项和第二项均为1&#xff0c;以后各项都为前两项之和。 公式如下&#xff1a; F(n) F(n-1) F(n-2) 其中&#xff0c;F(1) 0,…

CTP-API开发系列之十:v6.7.0-Python版封装(Windows/Linux)(附源码)

CTP-API开发系列之十&#xff1a;v6.7.0-Python版封装&#xff08;Windows/Linux&#xff09;&#xff08;附源码&#xff09; CTP-API开发系列之十&#xff1a;v6.7.0-Python版封装&#xff08;Windows/Linux&#xff09;&#xff08;附源码&#xff09;资源获取准备工作Windo…

实验2 芯片测试算法设计

一、【实验目的】 &#xff08;1&#xff09;理解分治策略的设计思想&#xff1b; &#xff08;2&#xff09;熟悉将伪码转换为可运行的程序的方法&#xff1b; &#xff08;3&#xff09;能够根据算法的要求设计具体的实例。 二、【实验内容】 有n片芯片&#xff0c;其中好芯片…

蓝桥杯每日一题:血色先锋队

今天浅浅复习巩固一下bfs 答案&#xff1a; #include<iostream> #include<algorithm> #include<cstring>using namespace std; typedef pair<int,int> PII;const int N510; int n,m,a,b; int dist[N][N]; PII q[N*N]; int hh0,tt-1;int dx[]{1,0,-1,…

蓝桥杯[OJ 1621]挑选子串-CPP-双指针

目录 一、题目描述&#xff1a; 二、整体思路&#xff1a; 三、代码&#xff1a; 一、题目描述&#xff1a; 二、整体思路&#xff1a; 要找子串&#xff0c;则必须找头找尾&#xff0c;找头可以遍历连续字串&#xff0c;找尾则是要从头的基础上往后遍历&#xff0c;可以设头…

【spring】@Import 注解学习

Import 介绍 Import 是 Spring 框架中的一个注解&#xff0c;用于导入配置类或组件。它可以将一个或多个配置类或组件导入到当前的配置类或组件中&#xff0c;从而实现配置的复用和组合。 在Spring Boot应用中&#xff0c;Import注解可以帮助我们更加灵活地组织和管理配置类。…

(学习日记)2024.03.09:UCOSIII第十一节:就绪列表

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

正点原子精英版TFTLCD代码移植

&#xff08;1&#xff09;将lcd.c和lcd.h加入到HEADWARE文件中 &#xff08;2&#xff09;将lcd.c加入到环境中 选择lcd.c即可。 &#xff08;3&#xff09;在FWLib中添加stm32f10x_fsmc.c

Spring Boot整合canal实现数据一致性解决方案解析-部署+实战

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1.前言 2.canal部署安装 3.Spring Boot整合canal 3.1数据库与缓存一致性问题…

一篇普通的生活周记

学习进度汇报&#xff1a; 这周主要是参考着视频敲完了一个vue2后台项目&#xff0c;主要是vue2element-ui,因为之前写项目的时候用过lay-ui&#xff0c;虽然是结合着node.js写的&#xff0c;但是大差不差&#xff0c;所以上手也很快。同时&#xff0c;学长发给我们了ruoyi项目…

【Vue3】Vue3中路由规则的 props 配置

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

页面侧边栏顶部固定和底部固定方法

顶部固定用于侧边栏低于屏幕高度----左侧边栏 底部固定用于侧边栏高于屏幕高度----右侧边栏 vue页面方法 页面布局 页面样式&#xff0c;因为内容比较多&#xff0c; 只展示主要代码 * {margin: 0;padding: 0;text-align: center; } .head {width: 100%;height: 88px;back…