数据结构与算法【递归】Java实现

递归

递归是一种解决计算问题的方法,其中解决方案取决于同一类问题的更小子集。

特点:

  • 自己调用自己,如果说每个函数对应着一种解决方案,自己调用自己意味着解决方案是一样的(有规律的)
  • 每次调用,函数处理的数据会较上次缩减(子集),而且最后会缩减至无需继续递归
  • 内层函数调用(子集处理)完成,外层函数才能算调用完成

递归二分查找

具体实现代码如下

    public int f(int[] a, int target, int i, int j) {if (i > j) {return -1;}int m = (i + j) >>> 1;if (target < a[m]) {return f(a, target, i, m - 1);} else if (a[m] < target) {return f(a, target, m + 1, j);} else {return m;}}

递归冒牌排序

未排区域为[0,j],已排区域为[j,数组长度-1]

具体实现代码如下

    public void bubbleSort(int[] a, int j) {if (j==0){return;}int temp;for (int i = 1; i < j; i++) {if (a[i-1] > a[i]) {temp = a[i-1];a[i-1] = a[i];a[i] = temp;}}bubbleSort(a,j-1);}

但是这种实现方式存在一定的效率问题。比如说需要排序的数组为[1,2,3,4,5,7,6]。那么经过排序之后,应该为[1,2,3,4,5,6,7]。只需要将6,7交换就好。存在大量无用循环,因此我们可以对冒泡排序进行改进。使用变量x默认为0的位置,并接收上一次交换的下标值。当一次递归x仍然为0,则说明已经没有需要排序的元素了,因此可以直接结束递归。具体实现代码如下

    public static void bubbleSort2(int[] a, int j) {if (j == 0) {return;}int temp;int x = 0;for (int i = 1; i < j; i++) {if (a[i - 1] > a[i]) {temp = a[i - 1];a[i - 1] = a[i];a[i] = temp;x = i;}}bubbleSort2(a, x);}

递归实现插入排序

未排区域为[low,数组长度-1],已排区域为[0,low]

具体实现代码为

    public static void insertSort(int[] a, int low) {//结束递归条件if (low == a.length) {return;}int i = low - 1;int temp = a[low];//结束排序条件,当i=-1与找到第一个小于temp元素时while (i >= 0 && a[i] > temp) {a[i + 1] = a[i];i--;}a[i + 1] = temp;insertSort(a, low + 1);}

多路递归

上面的递归实现有一个特点就是每个递归函数只包含一个自身的调用,这称之为单路递归。如果每个递归函数例包含多个自身调用,称之为多路递归。

多路递归的经典案例:斐波那契数列

递推关系如下:

简单实现

    public static int f(int n) {if (n == 0) {return 0;}if (n == 1) {return 1;}return f(n - 1) + f(n - 2);}

斐波那契数列的变种问题

兔子问题

 

  • 第一个月,有一对未成熟的兔子(黑色,注意图中个头较小)
  • 第二个月,它们成熟
  • 第三个月,它们能产下一对新的小兔子(蓝色)
  • 所有兔子遵循相同规律,求第n个月的兔子数

解决思路:设第 n 个月兔子数为 f(n)

  • f(n) = 上个月兔子数 + 新生的小兔子数
  • 而【新生的小兔子数】实际就是【上个月成熟的兔子数】
  • 因为需要一个月兔子就成熟,所以【上个月成熟的兔子数】也就是【上上个月的兔子数】
  • 上个月兔子数,即 f(n-1)
  • 上上个月的兔子数,即 f(n-2)

简单实现

    public static int rabbit(int n){if (n==1){return 1;}if (n==2){return 1;}return rabbit(n-1)+rabbit(n-2);}

青蛙爬楼梯

  • 楼梯有 n 阶
  • 青蛙要爬到楼顶,可以一次跳一阶,也可以一次跳两阶
  • 只能向上跳,问有多少种跳法

解决思路:因为最后一跳只能为1或是2,那么当从第三个台阶开始时,跳法等于一个台阶的跳法加两个台阶的跳法之和

斐波那契数列的优化

在之前的实现代码中,它的运算流程如下

可以看到,存在许多重复运算。因此我们可以对其进行记忆化(做缓存)

    public static int cache(int n) {int[] cache = new int[n + 1];Arrays.fill(cache, -1);cache[0] = 0;cache[1] = 1;return f1(cache, n);}public static int f1(int[] cache, int n) {if (cache[n] != -1) {return cache[n];}int x = f1(cache, n - 1);int y = f1(cache, n - 2);cache[n] = x + y;return cache[n];}

这种实现方式采用了以空间换取时间。

爆栈问题

每次调用方法时,JVM会给该方法分配一个内存空间,当递归次数过多时内存也会占用过多,当内存分配完毕,还要接着递归时,就会抛出StackOverflowError异常

尾调用

如果函数的最后一步是调用一个函数,那么称为尾调用,例如

function a() {return b()
}

下面代码不能叫做尾调用

function a() {const c = b()return c
}
  • 因为最后一步并非调用函数
function a() {return b() + 1
}
  • 最后一步执行的是加法

一些语言的编译器能够对尾调用做优化,例如

function a() {// 做前面的事return b() 
}function b() {// 做前面的事return c()
}function c() {return 1000
}

没优化之前的伪码

function a() {return function b() {return function c() {return 1000}}
}

优化后伪码如下

a()
b()
c()

相当于平级调用,而不是嵌套调用。之所以可以这样优化,是因为a的函数返回结果就是b的返回结果,那么a的内存可以直接释放。b的返回结果是c的返回结果,那么也可以直接释放b所占用的内存。

但是Java并不支持这种尾调用优化。因此需要避免递归次数过多导致的爆栈问题。

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

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

相关文章

算法——滑动窗口

什么是窗口&#xff1f;就是符合题目要求的区域内的数据&#xff0c;将每次符合数据的窗口内的数据记录下来&#xff0c;然后将窗口后移&#xff0c;寻找其他符合要求的数据&#xff0c;每次进入窗口和退出窗口都需要一定的要求 一、长度最小的子数组 LCR 008. 长度最小的子数…

element el-upload上传功能

2023.11.14今天我学习了如何使用el-upload: <!--drag设置可拖动--><!--accept".xlsx"设置上传的文件类型--><!--:limit1上传文件的最大个数--><!--:auto-upload"false"是否在选取后直接上传--><!--:before-upload"beforeU…

城市内涝对策,万宾科技内涝积水监测仪使用效果

随着城市化进程的加速&#xff0c;城市道路积水问题明显越来越多&#xff0c;给人们的出行和生活带来更多的不便。内涝积水监测仪作为高科技产品能够实时监测道路积水情况&#xff0c;为城市排水系统的管理和维护提供重要的帮助。 在城市生命线的基础设施规划之中&#xff0c;地…

第三天课程 RabbitMQ

RabbitMQ 1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&am…

python数据结构与算法-02_数组和列表

线性结构 本节我们从最简单和常用的线性结构开始&#xff0c;并结合 Python 语言本身内置的数据结构和其底层实现方式来讲解。 虽然本质上数据结构的思想是语言无关的&#xff0c;但是了解 Python 的实现方式有助于你避免一些坑。 我们会在代码中注释出操作的时间复杂度。 数…

LeetCode【923】三数之和的多种可能性

题目&#xff1a; 思路&#xff1a; https://www.jianshu.com/p/544cbb422300 代码&#xff1a; int threeSumMulti(vector<int>& A, int target) {//Leetcode923:三数之和的多钟可能//initialize some constint kMod 1e9 7;int kMax 100;//calculate frequenc…

亚马逊云AI大语言模型应用下的创新Amazon Transcribe的使用

Transcribe简介 语音识别技术&#xff0c;也被称为自动语音识别&#xff08;Automatic Speech Recognition&#xff0c;简称ASR&#xff09;&#xff0c;其目标是将人类的语音中的词汇内容转换为计算机可读的输入&#xff0c;例如按键、二进制编码或者字符序列。语音识别技术已…

论文浅尝 | 用于开放式文本生成的事实增强语言模型

笔记整理&#xff1a;李煜&#xff0c;东南大学硕士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://proceedings.neurips.cc/paper_files/paper/2022/hash/df438caa36714f69277daa92d608dd63-Abstract-Conference.html 1. 动机 生成式语言模型&#xff08;例如 GPT-3…

【机器学习基础】机器学习的模型评估(评估方法及性能度量原理及主要公式)

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;机器学习 欢迎订阅&#xff01;后面的内容会越来越有意思~ &#x1f4a1;往期推荐&#xff1a; 【机器学习基础】机器学习入门&#xff08;1&#xff09; 【机器学习基…

一文说清楚Openai的这波更新内容,大地震 一大波套壳公司倒闭

前几天Openai召开了首届的开发者大会&#xff0c;45分钟的会议&#xff0c;让千万用户感到兴奋&#xff0c;但是让万千的套壳的创业公司&#xff0c;却感觉如坐针毡。这次发布会发布了哪些功能&#xff1f;为什么会导致这种情况的发生&#xff1f;让我们接着往下讲 API升级且降…

Spring面试题:(七)Spring AOP思想及实现

AOP思想的概念 AOP的实现&#xff1a;动态代理技术 通过spring容器获取目标对象和增强对象&#xff0c;通过动态代理生产代理对象&#xff0c;在目标对象的目标方法执行增强方法&#xff0c;返回生成代理对象给spring容器&#xff0c;在获取bean时则获取代理对象。 JDK代理和…

虹科示波器 | 汽车免拆检修 | 2014款保时捷卡宴车行驶中发动机偶尔自动熄火

一、故障现象 一辆2014款保时捷卡宴车&#xff0c;搭载4.8L自然吸气发动机&#xff0c;累计行驶里程约为10.3万km。车主反映&#xff0c;行驶中发动机偶尔自动熄火&#xff0c;尤其在减速至停车的过程中故障容易出现。 二、故障诊断 接车后路试&#xff0c;确认故障现象与车主所…