递归超时怎么办?递归与递推的区别?递归的优化之道

2023-11-08

递归超时怎么办?递归的优化之道

平时在做题的时候,我们经常都要用到递归来解题,因为递归能最快速的让计算机知道我们想让他做什么,解放了我们的思维量(但在一定程度上加重了计算机的计算量,这也是可能超时的原因所在),方便我们阅读理解和修改。

这里我想再引用一下Leigh Caldwell在Stack Overflow上说的一句话:

“如果使用循环,程序的性能可能会更高,如果使用递归,程序可能更容易理解,如何选择要看什么对你来说更重要。”

如果你还不太理解递归,可以看我之前写过的一篇文章

再次深入理解递归——总结设计易错点


为什么会超时?

也许正在学习练习递归的你并没有碰到递归超时的情况,那可能的原因是题目的测试数据太小啦!你的计算机计算能力还算可以,所以就不存在超时的问题。

下面我以斐波那契数列举个例子让你理解下为什么会超时

我们都知道斐波那契数列从第三项开始每一项为前两项的和,那么如果我们要求第七项,那么流程如下:
在这里插入图片描述

嗯,也许你已经发现了不对的地方,我们重复计算了5两遍,4三遍,3四遍…如果项数很大,重复计算的次数只会越来越多

导致这样子的原因取决于递归的本质所决定的,我将此理解为触底才反弹。(下面会详细解释)


如何优化?

注意,我觉得这里要先特别区分一下递推和递归的区别,虽然只有一字之差,但意思和本质却截然相反。从小学数学的时候其实我们就已经接触了递推,就姑且还以斐波那切数列为例吧,我们只有知道了前面几项,比如1、2才能推第三项的值,只有知道了第三项和第四项的值才能推出第五项…以此类推递推到我们需要求的那个项,有点类似于滚大雪球吧,只能从小雪球滚起。

而递归呢,方向就是相反的(但最后求解过程中又包括递推),先告诉你,我要求第100项的值,嗯,这时候你就说“只要我知道第98项和第99项我就能求出第100项”,紧接着“只要我知道第97和98项我就能求出第99项”…等等等等这样一直把大问题(求第100项的值)逐渐化解为小问题(第99、98、…、1项的值),从上往下递归,直到递归到最低,也就是第一项时停止,然后原路一层层的把数值返回,从第一项第二项求出第三项,返回求出第四项(这里不就是递推吗)所以我更倾向于把递归理解为反向索引+递推。

总结来说递推就是自底向上,从下往上求解,而递归是从上往下,触底反弹,返回答案。

嗯,可能你已经有点感悟了,其实在大部分情况下递归都能用递推来实现,甚至在某些情况下递推还要更好,因为递推是记忆化的,而递归要看你如何设计了,如果不是记忆化的递归,那么每次都要触底才能反弹,也未免太浪费时间了。是的,在有时候碰到数据很大的时候,用递归固然可以直接解放你的思维量,让代码看上去很简洁,也易于别人阅读和修改,但随之而来的可能是空间的极度浪费或者根本就不够用啊,这时候你就要用递推了

我们还是以例题来说明吧

跳台阶——超时、递归、斐波那契

在这道题中,我们先看下我一开始写的递归代码:

#include <stdio.h>

int num( int n )
{
    int total = 0;
    int i;
    if ( n==1 )
        return n;
    else
    {
        for ( i=2; i<=n; i++ )
        {
            total += num(i-1);
        }
        total++;
        return total;
    }
}

int main()
{
    int t;
    scanf ( "%d", &t );
    while ( t-- )
    {
        int n;
        scanf ( "%d", &n );
        printf ( "%d\n",num(n) );
    }
    return 0;
}

推荐大家上机运行看看,当输入15、20等数据时几乎是秒出答案,但29、30时就要等上大概1秒的时间了,的确,刚刚上面已经给大家一个直观的理解了,重复计算的部分的确很多。

那这道题如何改成递推呢?(或者记忆化递归呢?)

先上代码:

#include <bits/stdc++.h>
using namespace std;

int stair[31];

int main()
{
	int t,n;
	int i,j;
	

	cin >> t;
	while ( t-- )
	{
		cin >> n;
		stair[1] = 1;
		stair[2] = 2;
		for ( i=3; i<=n; i++ )
		{
			if ( stair[i] == 0 )
			{
				for ( j=i-1; j>0; j-- )
				stair[i]+=stair[j];
				stair[i]++;
			}
		} 
				
		cout << stair[n] << endl;
	}
	
	return 0;

}

这个代码的思路就是将算好的数据储存在数组中,然后在大数据的时候直接调用,如果调用的时候还没算好就先算好再调用计算。这样子就相当于“记忆”下了递归的结果,不用每次都重新计算啦!

大部分的递归优化都可以通过记忆化递归或递推实现~

什么?你还是有点半蒙半懂的?没事,多自己练习优化就会啦,下面几个例子都是我自己踩过的雷,就是自己用递归超时被WA,用递推就AC的例子,都有较详细的解说并附上了递归和递推的代码

过河问题——动态规划的经典线性模型

广工大2020级年ACM第一次月赛——Dio的面包工坊


写在后面:

为了确保大家能够看懂我的文章,我尽量每字每句都详细讲解,给出证明和解释,真心希望你能够在阅读此篇文章后从中多多少少有所收获。因此每篇文章我都投入了大量的时间和精力,去举例,去说明,去分析,如果你觉得这篇文章写的还不错,点赞、关注和收藏一键三连就是对我最大的鼓励啦,我以后也会写更多更高质量的文章!也欢迎一起讨论指出文章可能存在的逻辑问题~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

递归超时怎么办?递归与递推的区别?递归的优化之道 的相关文章

  • 71-C语言-逆序拼接两个字符串

    问题 拼接字符串 拼接的那个字符串 需要先拼接 再连接 思路 两个字符数组 先创建出来并赋值 计算字符串的长度 随后弄两个指针 在一个for循环中 进行添加赋值 第一个数组从字母串末尾开始 然后让另一个数组的末尾处值给值到第一个数组中 随后
  • c——顺序结构

    顺序结构 1 赋值语句 2 数据的输出 3 数据的输入 4 复合语句与空语句 4 1 复合语句 4 2 空语句 5 程序实例 1 赋值语句 在赋值表达式的后面加上 分号 就构成了赋值语句 2 数据的输出 字符原样输出 指定宽度输出 如果长度
  • C语言求s=a+aa+aaa+aaaa+....

    问题描述 求s a aa aaa aaaa 其中a是一个数字 n表示a的位数 a和n由键盘输入 代码描述 方法1 include
  • C语言练习:通讯录

    通讯录代码 本文介绍如何使用C语言实现一个通讯录的功能 1 通讯录能存放1000个人的信息 每个人信息包含名字 年龄 性别 电话 地址 2 增加人的信息 3 删除指定人信息 4 查找指定人的信息 5 修改指定人的信息 通讯录包含三个部分 头
  • C4droid安装使用教程

    1 C4droid简介 手机 Android 上C C 的IDE 编译器 便携 功能强大 足以满足初学者平时的练习 汉化版 更易理解和使用 2 C4droid下载 在我分享的百度网盘链接中下载 https pan baidu com s 1
  • 10.在两个数之间,求素数

    问题 输入两个数 求在这两个数之间的素数 分析思路 素数 什么是素数 素数就是很朴素的一个数 它只跟1和自己玩 不跟其他数字玩 因此 它只可以被1和自身整除 1不是素数 1 从键盘输入两个数字 scanf 2 判断素数 需要用一个数字 从头
  • 【C语言学习笔记】算法篇——初见深度优先搜索

    基于啊哈 算法中的实例 题目 输入一个正整数n 输出1 n的全排列 代码分析 include
  • 【C语言】符号常量(#define)

    举例 define XY RX 100 用法 define 标识符 常量 其中 标识符一般全大写 单词之间用 下划线分割
  • C——编译预处理

    编译预处理 1 宏定义 2 文件包含 3 条件编译 C语言提供的预处理 在编译之前进行 主要有三种 宏定义 文件包含和条件编译 预处理命令不是C语句 不用加分号 1 宏定义 形式 define 宏名 替换文本 define 宏名 参数 替换
  • 程序设计的基本概念

    程序设计的基本概念 1 程序 2 结构化程序设计 1 程序 由高级语言编写的程序称为 源程序 由C语言编写的程序扩展名为 C 经过 编译 目标程序 后生成文件的扩展名为 obj 经过 链接 可执行程序 后生成文件的扩展名为 exe C语言源
  • win10搭建c语言开发环境

    win10搭建c语言开发环境 在window10上面用MingW搭建编写C语言的环境 1 下载Mingw 下载页面自行搜索 开始安装 安装路径自行选择 2 点击 continue 出现如下图 3 稍微等待一会 出现如下图界面 选择4项 然后
  • 48-C语言-输出十个数,并判断最大值和计算平均值

    问题 输入十个数 计算最大值和平均值 思路 输入十个数 在数组内输入 所以定义一个数组先 a 10 再进行输入 再for循环里 进行输入 scanf d a i 之后定义最大值和平均值 由于平均值需要先算出总和 所以再定义一个总和的变量 之
  • 76-C语言-输入班级学生的姓名和三科成绩,按总分排名

    问题 输入50分学生的姓名和三科成绩 按降序输出名字和总分 成绩相同的并列排名 思路 因为要学生排名 且一个学生有姓名 成绩 以及总分 所以弄一个学生的结构体 有多少学生 就输入该结构体的数组 一个for循环 给每个学生赋值 排序 降序 用
  • 52-C语言-文件问题-把字符串中的小写字母变为大写字母,并输出到磁盘文件“test”中,输入的字符串以‘!’结束

    问题 从键盘输入一个字符串 将其中的小写字母全部转换成大写字母 然后输出到一个磁盘文件 test 中保存 输入的字符串以 结束 思路 从键盘输入字符串 char str 100 gets str 将其中的小写字母变为大写字母 并且给大写字母
  • C——结构体

    结构体 1 自定义类型 2 结构体 2 1 结构体类型说明 2 2 结构体变量的定义 2 3 结构体的初始化 2 4 结构体变量所占空间大小 2 5 结构体成员的引用 3 链表 3 1 处理动态链表所需的函数 3 2 指向自身的结构体类型
  • 递归超时怎么办?递归与递推的区别?递归的优化之道

    递归超时怎么办 递归的优化之道 平时在做题的时候 我们经常都要用到递归来解题 因为递归能最快速的让计算机知道我们想让他做什么 解放了我们的思维量 但在一定程度上加重了计算机的计算量 这也是可能超时的原因所在 方便我们阅读理解和修改 这里我想
  • 41-C语言-蛇形矩阵-输出从1到n的矩阵

    问题 蛇形矩阵 构建蛇形矩阵 之后 根据条件相应输出 思路 蛇形矩阵 初始值a 0 0 为1 之后开始进行趟数变更 蛇形矩阵趟数 斜着来 斜杠 第一趟为a 0 1 和a 1 0 每一趟中的范围为0到tang次 如第一趟中a 0 1 已经赋值
  • C——选择结构

    选择结构 1 关系运算与逻辑运算 1 1 关系运算 1 2 逻辑运算 2 if语句 2 1 单分支的if语句 2 2 双分支的if语句 3 条件运算符 4 switch语句 1 关系运算与逻辑运算 C语言中的逻辑值 C语言将 非0 值当做值
  • 61-C语言-小猴吃桃问题

    问题 猴桑第一天兴高采烈地采了好多桃子 并且吃了一半 太好吃了 然后又多吃了一个 第二天又吃了一半多一个 以此类推 到第十天的时候 再想吃的时候就剩下1个桃子了 那么请问 第一天猴桑摘了多少桃子 思路 跟做数学题一样 先提取有用条件 1 到
  • C语言入门

    什么是C语言 C语言是一门通用计算机编程语言 广泛应用于底层开发 C语言的设计目标是提供一种能以简易 的方式编译 处理低级存储器 产生少量的机器码以及不需要任何运行环境支持便能运行的编程 语言 尽管C语言提供了许多低级处理的功能 但仍然保持

随机推荐

  • Unable to open debugger port

    今天在启动idea debug情形下报了如下错误 就是 Error running tomcat Unable to open debugger port 127 0 0 1 58946 java net SocketException s
  • java es score_elasticsearch系列(五)score

    概述 score在ES中有着很重要的作用 有了它才有了rank 是验证文档相关性的关键数据 score越大代表匹配到的文档相关性越大 官方解释 查询的时候可以用explain来展示score的计算过程 也可以增加format yaml来讲j
  • JAVA机试练习(2023华为od练手题PDF)

    目录 一 字符串操作 二 排序 三 栈 四 排列组合 五 双指针 六 深度搜索 七 二叉树 八 其他 1 HJ5进制转换 16变10 JAVA实现16进制转10进制 java十六进制转十进制 奥特曼下象棋的博客 CSDN博客 2 HJ3去除
  • Spring依赖注入的三种方式及其区别

    一 基于构造器的依赖注入 private final InventoryMapper inventoryMapper public InventoryController InventoryMapper inventoryMapper th
  • RocketMQ部署之动态设置JVM启动参数

    这里是weihubeats 觉得文章不错可以关注公众号小奏技术 文章首发 拒绝营销号 拒绝标题党 背景 线上的RocketMQ集群有运行一段时间了 比如测试环境和线上环境的RocketMQ集群部署的机器内存大小肯定不一样 所以可能要写多个部
  • PHP使用原生sql语句实现七天连续签到

    PHP原生使用原生sql语句实现七天连续签到 准备 一张放用户签到的数据表 字段包括id userid 用户id signtime 签到时间 时间戳 days 连续签到时间 七天连续签到 public function sign useri
  • MySQL 性能监控 4 大指标

    编者按 本文作者为 John Matson 主要介绍 mysql 性能监控应该关注的 4 大指标 文章系国内 ITOM 管理平台 OneAPM 编译呈现 MySQL 是什么 MySQL 是现而今最流行的开源关系型数据库服务器 由 Oracl
  • Temporary failure in name resolution 错误解决方法 运行sudo 命令 can not resovle host xxx

    Temporary failure in name resolution 错误解决方法 vim etc resolv conf nameserver 114 114 114 114 nameserver 8 8 8 8 运行sudo 命令
  • 你是否也无法在Thebrain 11中打开旧版数据?看看正确过渡新版方式

    TheBrain是一款与众不同的思维导图软件 其所有信息通过一个又一个的节点进行联系 最终形成一个杂而不乱的网状结构 一旦你搜索并点击一个想法后 与之相关的所有关联信息将一目了然 与传统的树形思维导图相比 TheBrain更有助于整合零散的
  • Robot Framework 学习(1)- 简单网站兼容性测试

    Robot Framework 简单网站兼容性测试 0 Robot Framework 简介 Robot Framework 是一个通用的自动化测试框架 主要用于 验收测试 和 验收测试驱动开发 ATDD 会其它文章中会详细介绍ATDD 它
  • SecureCRT发送AT指令

    1 首先安装驱动 MTK提供的驱动 会在设备管理器里面显示 2 打开secureCRT 选择连接类型为serial串口 3 设置secureCRT可以输入文本 4 然后就可以输入指令测试看看了 整个过程结束 但可能在第4步时输出没反应 这是
  • VS2017配置QT5.14.2

    一 QT下载 需要注意的是VS与QT的版本对应 VS2017对应的最新的QT版本是5 14 以后的版本适应的是VS2019 本文以VS2017 QT5 14为例 QT5 14 下载地址 Index of archive qt 5 14 5
  • 单片机——蜂鸣器(生日快乐歌)

    基础知识 改变单片机引脚输出波形的频率 就可以调整控制蜂鸣器音调 产生各种不同音色 音调的声音 改变输出电平的高低电平占空比 占空比是指一个周期内高电平所占的时间 则可以控制蜂鸣器的声音大小 单片机采用的是无源蜂鸣器 需要产生一定的脉冲才能
  • Acwing-873. 欧拉函数

    欧拉函数的证明使用了容斥原理 include
  • IDA反汇编之栈帧例释

    目录 1 例释环境和预备知识 1 1 运行环境 1 2 IDA版本 1 3 预备知识 2 函数调用约定 3 函数局部变量布局 4 函数栈帧示例 5 IDA栈视图 1 例释环境和预备知识 1 1 运行环境 本示例运行环境为Windows 10
  • VLAN(虚拟局域网)的用法与配置

    一 交换机的作用 区别集线器 HUB HUB为物理层设备 只能直接转发电流 交换机为数据链路层设备 可以将电流与二进制转换 实现了以下功能 1 无限的传输距离 2 彻底解决了冲突 所有接口可以同时收发数据 3 二层单薄 物理寻址 在一个交换
  • Eigen:基础入门到使用

    文章目录 一 基础介绍 1 1 安装 1 2 框架 二 矩阵基础 2 1 矩阵和向量 2 2 动态矩阵 2 3 定义 2 4访问矩阵元素 2 5 重置矩阵大小 2 6 怎么选择固定矩阵和动态矩阵 三 矩阵的运算 3 1 加法和减法 3 2
  • android studio jvm设置,如何使用gradle和kotlin为android studio设置jvm目标?

    尝试编译用kotlin编写的单元测试时出现以下错误 任务 app compileDebugUnitTestKotlin失败 无法将使用JVM target 1 7构建的字节码内联到使用JVM target 1 6构建的字节码中 请指定正确的
  • 一款可以完美替代浏览器自带起始页的新标签页插件:Wetab

    现在打开你们的浏览器 映入眼帘的是不是一片空白的自带起始页 或者是乱七八糟布满网站快捷方式的页面 Wetab新标签页是一款没有广告并且免费使用的浏览器插件 还原一个干净纯粹的浏览器体验 一 为什么要用wetab 本人已经被那些乱七八糟的起始
  • 递归超时怎么办?递归与递推的区别?递归的优化之道

    递归超时怎么办 递归的优化之道 平时在做题的时候 我们经常都要用到递归来解题 因为递归能最快速的让计算机知道我们想让他做什么 解放了我们的思维量 但在一定程度上加重了计算机的计算量 这也是可能超时的原因所在 方便我们阅读理解和修改 这里我想