递归算法和尾递归

article/2025/10/4 17:39:36

一、递归算法

1、递归算法思想

递归算法是在程序中不断反复调用(间接或直接地调用)自身,来达到解决问题的算法。

递归是一个方法在其方法体内调用自身方法的调用方式。递归调用分为两种情况:

  • 直接递归,即在方法中调用方法本身
  • 间接递归,即间接地调用一个方法

在递归中主方法又是被调方法。执行递归将反复调用其自身。每调用一层就进入新的一层。例如:阶乘问题,斐波那契数列求项等。

2、递归如何实现

数论思想:利用数学公式或者定理或者规律求解问题。

递归的关键就是要找出这个递归公式,并找到递归终止条件。

递归操作使用到了 数论&穷举&回溯&分治&递归算法思想。

编写递归方法时:

  • 必须使用 if语句强制在未执行递归前返回。
  • 必须要设置终止递归的条件。

特点:

递归经常会对某些问题重复调用,导致算法效率不高,时间复杂度和空间复杂度都很高,很容易导致栈溢出异常。

3、递归使用场景

使用递归时,场景必须满足下面条件:

(1)一个问题的解可以分解为几个子问题的解。

(2)这个问题与分解之后的子问题,求解思路完全一样。

(3)问题一定有一个最后确定的答案,即递归的终止条件。

4、递归优化

(1)使用非递归

理论上,所有的递归代码是一定可以转换成非递归的方式,即不使用递归。

(2)使用缓存
我们把子问题的运算结果保存起来,这样就可以减少重复计算次数,从而降低递归的时间复杂度。有点动态规划算法思想的味道。

(3)使用尾递归

5、什么是尾递归

  • 百度百科-尾递归:https://baike.baidu.com/item/%E5%B0%BE%E9%80%92%E5%BD%92/554682

1)什么是尾递归

如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归。

当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。

尾递归函数的特点: 就是在回归过程中不用做任何其他操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。

简单理解尾递归: 即就是倒着算,不需要在回溯了,因为我们每次会把中间结果带下去。

因为我们编译器在编译代码时,如果发现函数末尾已经没有操作了,这时候就不会创建新的栈,而且覆盖到前面去。

2)原理
当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。

下面对求阶乘问题,斐波那契数列求项使用递归求解。数据越界暂时不考虑。

二、阶乘问题

阶乘(factorial)是基斯顿·卡曼于1808年发明的运算符号。阶乘也是数学里的一种术语,指1乘以2乘以3乘以4持续到所要求的数。比如:n! = 123*…*(n-1)*n

0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120

阶乘递推的方法定义:

f(n) = n * f(n - 1) (n ≥2,n ∈ N*)
f(1) = 1

1、递归求解

	@Testpublic void testFactorial() {//int res = factorial(6);//System.out.println(res);for (int i = 1; i <= 6; i++) {long start = System.currentTimeMillis();System.out.println(i + ":" + factorial(i) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解:时间复杂度O(2^n)和空间复杂度O(2^n)* @param n* @return*/private static int factorial(int n) {if (n <= 1) {return 1; // 递归的终止条件}return n * factorial(n - 1); // 继续递归的过程}

2、优化递归

(1)使用非递归

	@Testpublic void testNoRecurve() {for (int i = 1; i <= 6; i++) {long start = System.currentTimeMillis();System.out.println(i + ":" + noRecurve(i) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解优化:使用非递归。时间复杂度O(n)和空间复杂度O(n)* @param n*/private static int noRecurve(int n){if(n <= 1){return 1;}int a = 1;int res = 0;for (int i = 2; i <= n; i++) {res = i * a;a = res;}return res;}

(2)使用缓存

	@Testpublic void testUseCache() {for (int i = 1; i <= 6; i++) {resultData = new int[i + 1];long start = System.currentTimeMillis();System.out.println(i + ":" + useCache(i) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解优化:使用缓存。时间复杂度O(n)和空间复杂度O(n)* @param n*/private static int resultData[]; // 初始换全部是0private static int useCache(int n){if(n <= 1){return 1;}if(resultData[n] > 0){return resultData[n];}int res = n * useCache(n - 1);resultData[n] = res;return res;}

(3)使用尾递归

	@Testpublic void testTailRecurve() {for (int i = 1; i <= 6; i++) {long start = System.currentTimeMillis();System.out.println(i + ":" + tailRecurve(i, 1) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解优化:使用尾递归。时间复杂度O(n)和空间复杂度O(n)* @param n* @param res - 每次保存的结果。倒着算,初始值为终止条件值* @return*/public static int tailRecurve(int n, int res) {//System.out.println(n + ":" + res);	// 倒着算的结果if (n <= 1) {return res;}return tailRecurve(n - 1, n * res);	//倒着算}

三、斐波那契数列求项

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……。这个数列,第一和第二项为1,从第 3项开始,每一项都等于前两项之和。

斐波那契数列递推的方法定义:

f(n) = f(n - 1) + f(n - 2) (n ≥ 3,n ∈ N*)
f(1) = f(2) = 1

1、递归求解

	@Testpublic void testFibonacci(){//int res = fibonacci(6);//System.out.println(res);for (int i = 1; i <= 45; i++) {long start = System.currentTimeMillis();System.out.println(i + ":" + fibonacci(i) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解:时间复杂度O(2^n)和空间复杂度O(2^n)* * @return*/private static int fibonacci(int n) {if (n <= 2) {return 1; // 递归的终止条件}return fibonacci(n - 1) + fibonacci(n - 2); // 继续递归的过程}

2、优化递归

(1)使用非递归

	@Testpublic void testNotRecurve(){for (int i = 1; i <= 45; i++) {long start = System.currentTimeMillis();System.out.println(i + ":" + notRecurve(i) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解优化:使用非递归。时间复杂度O(n)和空间复杂度O(n)* @param n*/private static int notRecurve (int n){if(n <= 2){return 1;}int a = 1;int b = 1;int res = 0;for (int i = 3; i <= n; i++) {res = a + b;a = b;b = res;}return res;}

(2)使用缓存

	@Testpublic void testUseCache() {for (int i = 1; i <= 45; i++) {resultData = new int[i + 1];long start = System.currentTimeMillis();System.out.println(i + ":" + useCache(i) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解优化:使用缓存。时间复杂度O(n)和空间复杂度O(n)* @param n*/private static int resultData[]; // 初始换全部是0private static int useCache(int n){if(n <= 2){return 1;}if(resultData[n] > 0){return resultData[n];}int res = useCache(n - 1) + useCache(n -2);resultData[n] = res;return res;}

(3)使用尾递归

	@Testpublic void testTailRecurve() {for (int i = 1; i <= 45; i++) {long start = System.currentTimeMillis();System.out.println(i + ":" + tailRecurve(i, 1, 1) + " 所花费的时间为" + (System.currentTimeMillis() - start) + "ms");}}/*** 递归求解优化:使用尾递归。时间复杂度O(n)和空间复杂度O(n)* @param n* @param res - 每次保存的结果,初始值为终止条件值。* @param pre - 上次保存的结果,初始值为终止条件值。* @return*/public static int tailRecurve(int n, int res, int pre) {  if (n <= 2) {return res;}return tailRecurve(n - 1, res + pre, res);	//倒着算}

在这里插入图片描述

– 求知若饥,虚心若愚。


http://chatgpt.dhexx.cn/article/gEIlrboU.shtml

相关文章

尾递归(Tail Recursion)

向上递归(Pass-Up Recursion) 先来看一下传统的递归 def sum(lst):"""把lst所有元素求和"""if len(lst) 0:return 0return lst[0] sum(lst[1:])加入sum([1,2,3]),则是先求sum([2,3])&#xff1b;而要求sum([2,3]),则要求sum([3]), 求sum([3]…

尾递归

什么是尾递归 什么是尾递归呢?(tail recursion), 顾名思议&#xff0c;就是一种“不一样的”递归&#xff0c;说到它的不一样&#xff0c;就得先说说一般的递归。对于一般的递归&#xff0c;比如下面的求阶乘&#xff0c;教科书上会告诉我们&#xff0c;如果这个函数调用的深…

【算法】递归:递归优化之尾递归

【算法】递归&#xff1a;递归优化之尾递归 引言&#xff1a;在以往我发过一篇过于通过分析法去理解递归求解递归的博客文章&#xff0c;那篇文章主要介绍了如何去求解递归问题。而在这篇文章中&#xff0c;我会介绍一下如何去优化递归&#xff0c;顺带还会去分析一下递归算法的…

DDD微服务架构设计第四课 DDD指导微服拆分和落地实现

07 在线订餐场景中是如何开事件风暴会议的&#xff1f; 微服务设计最核心的难题是微服务的拆分&#xff0c;不合理的微服务拆分不仅不能提高研发效率&#xff0c;反倒还使得研发效率更低&#xff0c;因此要讲究“小而专”的设计。“小而专”的设计意味着微服务的设计不是简单拆…

微服架构的论述?

搭建微服架构 一、什么是微服架构 简单的说就是将一个整体的应用按照一定的规则拆分成一个个独立的应用&#xff0c;这些独立的应用后面又组合成了一个整体的应用。 比如说一个博客系统&#xff0c;我可能包含了发表文章&#xff0c;用户登录&#xff0c;用户评论等功能&…

微服务架构是什么?

微服务架构是什么&#xff1f; 一文详解微服务架构最初的需求随着业务发展……是时候做出改变了没有银弹监控 - 发现故障的征兆定位问题 - 链路跟踪分析问题 - 日志分析网关 - 权限控制&#xff0c;服务治理服务注册于发现 - 动态扩容熔断、服务降级、限流熔断服务降级限流 测试…

SpringCloud微服之Nacos的学习

1:使用前提 第一步:解压启动Nocos SpringCloudAlibaba 推出了一个名为 Nacos 的注册中心&#xff0c;在国外也有大量的使用。 startup.cmd -m standalone 访问http://localhost:8848/nacos/ 第二步:服务注册 工程目录 在父工程中添加依赖 <dependency><groupI…

python注册nacos微服并使用gateway网关

业务需求&#xff1a;使用python flask框架和java spring boot框架共同注册到nacos中&#xff0c;在由spring cloud gateway分配路由。 flaskDome: from flask import Flaskapp Flask(__name__)app.route(/python) def test():return "这是python flask框架接口&#xf…

大开眼界,Jenkins结合SpringCloud+K8S,打通微服一条龙技术讲解

Jenkins 是目前最常用的持续集成工具&#xff0c;拥有近50%的市场份额&#xff0c;他还是很多技术团队的第一个使用的自动化工具。由此可见他的重要性&#xff01; 这份Jenkins宝典从入门介绍到结合DockerSpringCloudKubernetes&#xff0c;打通一条龙技术讲解&#xff0c;简直…

基于Ant DesignPro Vue + SpringBoot 前后端分离 - 后端微服化 + 接口网关 + Nacos

基于Ant DesignPro Vue SpringBoot 前后端分离 - 后端微服化 接口网关 Nacos 通过Ant DesignPro Vue SpringBoot 搭建的后台管理系统后&#xff0c;实现了前后端分离&#xff0c;并实现了登录认证&#xff0c;认证成功后返回该用户相应权限范围内可见的菜单。 后端采用Spri…

微服务消费端通过feign调用微服异常问题

在项目开发中,我们的调用方通过Feign调用微服时,如果微服出现业务异常(例如空指针,或抛出自定义的异常)和非业务异常(参数不合法4xx异常)都会进入到调用方的全局异常拦截器,抛出的code全部转换成了500,这样不友好 实际上只有业务异常feign才会转换成500错误且转成FeignExcepti…

大开眼界!Jenkins结合SpringCloud+K8S,打通微服一条龙技术讲解

Jenkins 是目前最常用的持续集成工具&#xff0c;拥有近50%的市场份额&#xff0c;他还是很多技术团队的第一个使用的自动化工具。由此可见他的重要性&#xff01; 这份Jenkins宝典从入门介绍到结合DockerSpringCloudKubernetes&#xff0c;打通一条龙技术讲解&#xff0c;简直…

从0搭建公司SpringCloud Alibaba 微服務鉴权中心服务(详细教程)

大家好&#xff0c;我是宝哥&#xff01; 鉴权中心服务 认识JWT json web token 是一个开放的标准 &#xff0c;它定义了一个种紧凑的&#xff0c;自包含的方式&#xff0c;用于作为json对象在各方之间安全的传输信息 服务器鉴权完成之后 会生成 json 对象 发送给客户端&#x…

腾讯云开源业界微服管理框架 Femas

导读 企业数字化向云原生演进过程面临诸多痛点&#xff0c;微服务框架不统一、协议多样化、语言异构&#xff0c;纷繁复杂的微服务技术栈&#xff0c;基础组件之间像一座座孤岛&#xff0c;各个基础组件的控制面不能互联&#xff0c;让用户的体验非常割裂&#xff0c;各种历史包…

基于Ant DesignPro Vue + SpringBoot 前后端分离 - 后端微服化 + 接口网关 + Nacos + Sentinel

基于Ant DesignPro Vue SpringBoot 前后端分离 - 后端微服化 接口网关 Nacos Sentinel 通过Ant DesignPro Vue SpringBoot 搭建的后台管理系统后&#xff0c;实现了前后端分离&#xff0c;并实现了登录认证&#xff0c;认证成功后返回该用户相应权限范围内可见的菜单。 后…

分布式/微服务必配APM系统,SkyWalking让你不迷路

前言 如今分布式、微服务盛行&#xff0c;面对拆分服务比较多的系统&#xff0c;如果线上出现异常&#xff0c;需要快速定位到异常服务节点&#xff0c;假如还用传统的方式排查肯定效率是极低的&#xff0c;因为服务之间的各种通信会让定位更加繁琐&#xff1b;所以就急需一个…

大开眼界,Jenkins 结合 SpringCloud+K8S,打通微服一条龙技术讲解

Jenkins 是目前最常用的持续集成工具&#xff0c;拥有近 50%的市场份额&#xff0c;他还是很多技术团队的第一个使用的自动化工具。由此可见他的重要性&#xff01; 这份 Jenkins 宝典从入门介绍到结合 DockerSpringCloudKubernetes&#xff0c;打通一条龙技术讲解&#xff0c;…

【微服】单体、SOA、微服务

单体架构 将所有的功能都集中在一个模块中&#xff08;WAR包&#xff09;开发、部署、迭代&#xff0c;牵一发而动全身&#xff0c;局部低效率拖垮整个服务。 SOA 按服务对项目拆分&#xff0c;通过对外提供接口的方式提供服务&#xff0c;缓解了单体的单服务低效率拖垮整个服…

微服架构基础设施环境平台搭建 -(三)Docker+Kubernetes集群搭建

微服架构基础设施环境平台搭建 -&#xff08;三&#xff09;DockerKubernetes集群搭建 通过采用微服相关架构构建一套以KubernetesDocker为自动化运维基础平台&#xff0c;以微服务为服务中心&#xff0c;在此基础之上构建业务中台&#xff0c;并通过Jekins自动构建、编译、测试…

微服架构基础设施环境平台搭建 -(二)Docker私有仓库Harbor服务搭建

微服架构基础设施环境平台搭建 -&#xff08;二&#xff09;Docker私有仓库Harbor服务搭建 通过采用微服相关架构构建一套以KubernetesDocker为自动化运维基础平台&#xff0c;以微服务为服务中心&#xff0c;在此基础之上构建业务中台&#xff0c;并通过Jekins自动构建、编译、…