使用迭代优化递归程

封面:从斐波那契数列到递归.png

王有志,一个分享硬核Java技术的互金摸鱼侠
加入Java人的提桶跑路群:共同富裕的Java人

今天我们将会分析上篇文章中递归算法存在的问题,并通过迭代去优化。

递归存在的问题

上一篇中,我们计算了序号10以内的斐波那契数。今天为了清晰的展示递归解法存在的问题,我们试着计算序号为50的斐波那契数,如果电脑的性能较差的话,就不要尝试了。
图1:斐波那契数列执行时间.png
可以看到,从计算F(40)开始,递归解法的耗时如同“坐火箭”般上升,这种情况是我们无法忍受的。
在上一篇文章中,我们有一个练习就是优化递归求解第n个斐波那契数。那么今天,我们就一起看看通过递归求解,问题出现在哪里?
通过之前构建的F(6)的递归树,我们可以看到,递归求解斐波那契数时,存在大量重复的计算,例如:仅仅是计算F(2)就出现了5次。
那么比较容易想到的优化方案就是缓存计算结果,使用时再取出。因此我们引入缓存,减少重复计算,提升执行速度(和复杂度分析中的空间换时间呼应上了)。
我们来看下具体实现:

private static long fib_recursion_memory(int n) {
return fib_recursion_memory(n, new long[n + 1]);
}private static long fib_recursion_memory(int n, long[] memory) {if (n < 2) {return n;}if (memory[n] != 0) {return memory[n];}memory[n] = fib_recursion_memory(n - 1, memory) + fib_recursion_memory(n - 2, memory);return memory[n];
}

代码并不复杂,通过引入long类型数组,记录已经计算过的斐波那契数,以达到减少重复计算的目的。
除此之外,还有没有其他方法求解第n个斐波那契数?

迭代

我们在使用递归时,通常是将规模较大的问题拆分为规模较小的问题,依次求解组合的过程。反之我们也可以从最小规模的问题开始,逐步累积到规模较大的问题。
迭代正是这样一种方法。迭代是数学概念引入编程中的,非常容易与循环混淆。来看下百度百科中的定义:

迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。

既然说到非常容易与循环混淆,那么再来看看循环的定义:

循环是程序设计语言中反复执行某些代码的一种计算机处理过程,常见的有按照次数循环和按照条件循环。

通过定义我们很容易看出迭代与循环的差别,循环只是程序的重复执行,对计算结果没有要求,而迭代需要将每次计算结果应用到下一次循环中,所以可以说迭代只循环的子集

迭代求解第n个斐波那契数

事实上,递归都是可以改写为迭代的形式(不那么优雅),而且邓俊峰老师也在《数据结构》中说到:

实际上,属于尾递归形式的算法,均可以简捷地转换为等效的迭代版本。

这给了我们使用迭代改写递归求解斐波那契数列的理论基础,下面我们直接开始。
首先斐波那契数列的的递推公式是从第3项开始,因此我们需要处理第1,2项的特殊情况,代码如下:

if(n <= 0) {return 0
}if(n < 3) {return 1;
}

根据递推公式,如果计算n的值,我们需要知道 n−1 和 n−2 的值,那么我们声明3个变量,用p代表 n−2 ,用q代表 n−1 ,用result代表n,那么p设置为 F(1) ,q设置为 F(2) ,result设置为 F(3) ,代码如下:

int p = 1, q = 1, result = p + q;

现在我们已经有了3个变量,表示 F(1) 到 F(3) ,仅仅是这些,你已经可以计算出F(4)的值了:

p = q;
q = result;
result = p + q;

如果想要计算 F(n) 的值,我们只需要不断的重复计算 F(4) 的过程即可:

for(int i = 4; i <= n; i++) {p = q;q = result;result = p + q;
}

完整的代码如下:

private static int fib(int n) {
if(n <= 0) {return 0;
}if (n < 3) {return 1;
}int p = 1, q = 1, result = p + q;
for (int i = 4; i <= n; i++) {p = q;q = result;result = p + q;
}
return result;
}

随着循环的进行,每次计算的结果都会带入到下一次的循环,这就是定义中所指的每次迭代结果作为下一次迭代的初始值进行处理
至于通过迭代求解的斐波那契数列的时间复杂度,相信大家一眼就可以看出来了吧?
如果你了解动态规划的话,你很容易就能想到,迭代求解斐波那契数列就是简单的动态规划解法,不过这是后话,现在我们按下不表。

结语

今天的内容到这里就结束了,我们来回顾下都聊了哪些内容:
首先是回顾了递归求解斐波那契数列的问题,通过“记忆”优化了递归的执行速度,但是增加了空间复杂度。
然后为了更高效,我们引入了迭代,虽然我并不鼓励大家记忆概念和定义,但是你要明白相似概念的区别。
最后我们通过迭代的方式求解斐波那契数列,实现了 O(n) 复杂度。当然,斐波那契数列还有 O ( log ⁡ _ n ) O(\log\_{}{n}) O(log_n) 的解法,不过这不是我们今天的内容。

练习

我们来做几道简单的题目:

  • 剑指Offer 10-I 斐波那契数列
  • 53.最大子数组和
  • 70.爬楼梯
  • 121.买卖股票的最佳时机

如果最第53题和121题没有解出来也并没有关系,这里涉及到动态规划的知识,还没有接触到的小伙伴也不要着急,后面是有动态规划的专题的。


如果本文对你有帮助的话,还请多多点赞支持。如果文章中出现任何错误,还请批评指正。最后欢迎大家关注分享硬核Java技术的金融摸鱼侠王有志,我们下次再见!

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

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

相关文章

解析IT运维领域ITSS和ITIL证书

&#x1f33b;IT运维领域ITSS和ITIL证书是两种广泛认可的专业认证。 &#x1f4d7;ITSS认证证书 ITSS是中国电子技术标准化研究院推出的&#xff0c;&#x1f449;包含“IT 服务工程师”和“IT 服务经理”的系列培训。有效满足GB/T 28827.1 的符合性评估要求和ITSS服务资质升级…

软件测试|MySQL ORDER BY详解:排序查询的利器

简介 在数据库中&#xff0c;我们经常需要对查询结果进行排序&#xff0c;以便更好地展示数据或满足特定的业务需求。MySQL提供了ORDER BY子句&#xff0c;使我们能够轻松地对查询结果进行排序。本文将详细介绍MySQL ORDER BY的用法和示例&#xff0c;帮助大家更好地理解和应用…

74应急响应-winlinux分析后门勒索病毒攻击

#操作系统(windows,linux)应急响应&#xff1a; 1.常见危害&#xff1a;暴力破解&#xff0c;漏洞利用&#xff0c;流量攻击&#xff0c;木马控制(Webshell,PC 木马等)&#xff0c;病毒感染(挖矿&#xff0c;蠕虫&#xff0c;勒索等)。 2.常见分析&#xff1a;计算机账户&…

有趣的前端知识(二)

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读HTML元素元素属性头部元素列表元素区块元素表单元素 颜色字符实体 HTML元素 …

从零学算法17

17.给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits “23” 输出&#xff1a;[…

加速科技ST2500 数模混合信号测试设备累计装机量突破500台!

国产数字机&#xff0c;测试中国芯&#xff01;新年伊始&#xff0c;国产半导体测试设备领军企业加速科技迎来了振奋人心的一刻&#xff0c;ST2500 数模混合信号测试设备累计装机量突破500台&#xff01;加速科技凭借其持续的创新能力、完善的解决方案能力、专业热忱的本地化服…

论文阅读:Making Large Language Models A Better Foundation For Dense Retrieval

论文链接 Abstract 密集检索需要学习区分性文本嵌入来表示查询和文档之间的语义关系。考虑到大型语言模型在语义理解方面的强大能力&#xff0c;它可能受益于大型语言模型的使用。然而&#xff0c;LLM是由文本生成任务预先训练的&#xff0c;其工作模式与将文本表示为嵌入完全…

Git将本地项目上传到Gitee仓库

1.右键点击文件&#xff0c;点击Git Bash Here,进入git窗口 2.初始化本地仓库 git init3.将本地仓库与远程仓库建立连接 git remote add origin 远程仓库地址远程仓库地址在gitee仓库复制即可 4.将远程仓库的文件拉到本地仓库中 git pull origin master5.将本地文件全部上传…

RocketMQ5-03RocketMQ-Dashboard和Java客户端访问示例

接上篇02快速部署RocketMQ5.x(手动和容器部署) 已经完成 RocketMQ5.0 环境的部署&#xff0c;就需要对这个环境进行测试&#xff0c;查看集群、写入消息、读取消息等 本篇教你如何使用和查看部署的服务&#xff1a; Docker部署 Dashboard 获取镜像并下载部署服务 客户端连接 …

python画房子

前言 今天&#xff0c;我们来用Python画房子。 一、第一种 第一种比较简单。 代码&#xff1a; import turtle as t import timedef go(x, y):t.penup()t.goto(x, y)t.pendown() def rangle(h,w):t.left(180)t.forward(h)t.right(90)t.forward(w)t.left(-90)t.forward(h) de…

Stable Diffusion 系列教程 - 6 Dreambooth及训练

Stable-Diffusion、Imagen等文生图大模型已经具备了强大的生成能力&#xff0c;假设我们的Prompt为 [Cyberpunk Style]&#xff0c;SD或许能很快画出赛博朋克风格的一幅画。但你作为一个不知名的人&#xff0c;不能奢求SD在训练的时候把你自己想要的风格也加进去吧&#xff1f;…

鸿蒙应用开发学习路线(OpenHarmony/HarmonyOS)

鸿蒙应用开发学习路线&#xff08;OpenHarmony/HarmonyOS&#xff09; HarmonyOS应用开发学习路线网站汇总社区汇总视频学习路线 OpenHarmony应用开发学习路线与资料网站汇总社区汇总学习路线 MarkDown工具推荐 HarmonyOS应用开发学习路线 作者&#xff1a;坚果 团队&#xff1…