《JavaSE-习题篇二》之七个题目,十六张图,让你不惧递归。

简介: 《JavaSE-习题篇二》之七个题目,十六张图,让你不惧递归。

前言

学习方法后,我们来学习一种特殊调用方法的方式,即递归。本篇文章将介绍什么是递归,以及递归的使用规则和注意事项,最后通过几道经典的题目来加深对递归的理解。

导航助手

🏆1.生活中的递归

🎲1.1永不终结的故事

🏆2.什么是方法递归?

🎲2.1递归的必要条件:

🎲2.3递归题目

✔2.3.1递归求 N 的阶乘

✔2.3.2按顺序打印一个数字的每一位(例如 123打印出 1 2 3

✔2.3.3递归求 1 + 2 + 3 + ... + 10

✔2.3.4写一个递归方法,输入一个非负整数,返回组成它的数字之和.

✔2.3.5求斐波那契数列的第 N 项

✔2.3.6汉洛塔

✔2.3.1青蛙跳台阶

3.总结

🏆1.生活中的递归

🎲1.1永不终结的故事

从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:
从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:
从前有座山,山上有座庙...
"从前有座山……"

老和尚不再念经了,改行讲故事了,但是这故事无限的重复开始,实在是比念经还难受。

##🎲 1.2蒙娜丽莎之蒙娜丽莎

屏幕截图 2023-08-08 063359.png

画中蒙娜丽莎又拿了一幅蒙娜丽莎的画,所以有多少个蒙娜丽莎?你知道吗?我不知道。

以上的两个例子都有一个共同的特征:自身中又包含自身,该种思想在数学和编程中非常有用,因为有时,我们遇到的一一些问题并不太好解决,这时我们可以将一个原问题分解为多个子问题,当发现原问题与子问题有相同的解法时,我们只需要将子问题都解决,那么原问题自然也解决了。

🏆2.什么是方法递归?

定义:一个方法直接或者间接的调用自己的形式称之为递归

递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式.

例如, 我们求 N!

起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.

递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!

🎲2.1递归的必要条件:

1.将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同

2.递归出口

了解什么是递归后,我们先来看一个错误使用递归的例子,加深我们对递归的理解

print()方法,在自己的方法体里面又调用了自己,没有任何的终止条件如同老和尚给小和尚故事,永远都讲不完。而在程序中方法或者函数实在栈区开辟内存来使用的,栈的空间有限的,而该print()方法可以无限递归下去,即无限制开辟栈的内存,最终栈的空间耗尽,会发生栈溢出

栈溢出就是递归没有设置终止条件导致死递归了,简称“死龟”。

由上述错误的例子我们可以得出一个结论,那就是递归是需要一个终点,不然就“死龟”。

我们依然使用上述代码,给print();方法传递一个参数,当该参数为1时则终止递归。

了解递归与递归使用的注意事项后,我们来使用递归做几个题目。

🎲2.3递归题目

✔2.3.1递归求 N 的阶乘

递归公式的推导

我们都知道4的阶乘是对于1至4的每个数的乘积,3的阶乘是对于1至3的每个数的乘积,从中我们会发现4的阶乘其实可以写成4乘3的阶乘,3的阶乘可以写成3乘2的阶乘,当我们发现这个规律之后便可以推导出求N的阶乘的递归公式即:N=N * (N-1)。

public class Demo2 {
    public static void main(String[] args) {
        System.out.println("请输入一个整数:");
        Scanner sc = new Scanner(System.in);
        int num=sc.nextInt();
        int ret= factor(num);
        System.out.println(ret);
    }
    public static int  factor(int n){
        if(n==1){
            return 1;
        }
        int ret=n*factor(n-1);
        return ret;
    }
}

n=1就是求N递归的终止条件也是回溯的起点,我们将上述代码通过画图的方式展开递归来分析。

红色的线为递推的过程,绿色的线为回归的过程,由图可以清晰的知道,n=1即是递归结束的条件,恰恰也是递归开始的条件。

注意

  1. 1.每次执行到方法体中调用自己的时候,会重新进去方法从头再来。
  2. 2.当递归回来时想要继续这行代码,所以我们使用一个变量存在这个结果。

递归递归相当于在方法体内执行一半,又重新开始了。

✔2.3.2按顺序打印一个数字的每一位(例如 123打印出 1 2 3

递归公式的推导

数字如果是一位直接输出即可,数组如果大于等于两位以上,我们可以通过/和%来得到相应数字,下面的代码至于为什么是先/,我们可以逆向思维思考,首先我们要输出123,1是最先打印出来的,而根据递归1既是终止条件也是回归的条件,只要先不断/,最后回归的时候%一下再输出才能得到123。如果还没有明白我们画图来理解。

代码

public class Demo3 {
    public static void main(String[] args) {
        Scanner sc= new Scanner(System.in);
        System.out.println("请输入一个数:");
        int num=sc.nextInt();
        print(num);
    }
    public static void print(int n){
        if(n<10){
            System.out.print(n);
        }else{
            print(n/10);
            System.out.print(n%10);
        }
    }
}
✔2.3.3递归求 1 + 2 + 3 + … + 10

递归公式推导

我们求1到10的和,相当于要求10+1到9的和,所以我们要求1到n的和就是求n+n-1的和,即公式为n+sum(n-1).

代码

public class Demo5 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        System.out.println(sum(n));
    }
    public static  int sum(int n ){
        if(n==1){
            return 1;
        }
        return n+sum(n-1);
    }
}

✔2.3.4写一个递归方法,输入一个非负整数,返回组成它的数字之和.

例如,输入 1729, 则应该返回 1+7+2+9,它的和是19

递归公式

比如123的各个位数的之和,sum=123%10+123/10%10+123/100%10,要求n的每一位数的和n%10+sum(n/10).

代码

public class Demo4 {
    public static void main(String[] args) {
        Scanner sc= new Scanner(System.in);
        int num = sc.nextInt();
        System.out.println(sum(num));
    }
    public static int sum(int num){
        if(num<10){
            return num;
        }
        return num%10+sum(num/10);
    }
}
✔2.3.5求斐波那契数列的第 N 项

斐波那契数列介绍

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

递归斐波纳契数列

斐波纳契数列递推公式为F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N),F(0)=1,F(1)=1.

public class Demo6 {
    public static void main(String[] args) {
       for(int i=0;i<10;i++){
           System.out.print(  fib( i )+" ");
       }
    }
    public static  int  fib(int n ){
        if(n==0||n==1){
            return n;
        }
        int tmp=fib(n-1)+fib(n-2);
        return tmp;
    }
}

结果图

因为我们已知的数只有F(0)=0,F(1)=1.当N大于等于2时,F(n)=F(n - 1)+F(n - 2),会一直不断,F(n)=F(n - 1)+F(n - 2),直到F(0)=1,F(1)=1,才会回归,这样计算有大量重复的计算,当N是一个很大的数字,计算机就要重复的计算很久,为了解决重复计算的问题,我们可以使用循环来求斐波纳契数列。

循环求斐波纳契数列

我们定义三个变量,f1和f2分别标记斐波那契数数列的第一和第二项,f3先置为-1,用来记录F(n - 1)+F(n - 2)。

public class Demo7 {
    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            System.out.print(fib(i)+" ");
        }
    }
    public static  int fib(int n){
        if(n<0){
            return -1;
        }
        if(n==0||n==1){
            return n;
        }
        int f1=0;
        int f2=1;
        int f3=-1;
        for(int i=2;i<=n;i++){
            f3=f1+f2;//第三项和
            f1=f2;
            f2=f3;
        }
        return f3;
    }
}

✔2.3.6汉洛塔

汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

假设有三个柱子分别为A,B,C。A柱子是盘子的起始位置,B柱子是盘子的中转位置,C柱子是目标位置。我们也不先考虑64个盘子而考虑1个盘子,我们会发现只有一个盘子我们只需将A上的盘子直接搬到C柱子。

当只有1个盘子时

移动顺序:A -> C

当只有2个盘子时

移动顺序:A ->B A->C B->C

当只有3个盘子时

移动顺序: A ->C A->B C->B A->C B->A B->C A->C

通过这三种情况,我们可以得知只有一个盘子里需要移动1次,2个盘子时需要移动3次,4个盘子是需要移动7次,由此我们可以得出盘子个数(N)于移动的次数之间的关系为2的N次方-1.同时我们也可以知道当盘子数N大于等于2时,我们可以将它成2个盘子,即最底下为一个,上面N-1为一个盘子,我们会发现我们是先将N-1个盘子先通过C柱子移动B柱子,然后将最底下的盘子移动到C柱子,然后将B柱子上的盘子通过A柱子移动到C柱子。

代码思路:我们为了打印移动轨迹,我们写个move();方法, 当只有1个盘子时,直接从A移动到C,当大于等于时我们将需要使用递归将N-1盘子从A柱子通过C柱子移动到B柱子,又将B柱子上所有的盘子通过A柱子移动到C柱子。

代码

public class Demo8 {
    public static void main(String[] args) {
        int num=3;
        hanio(3,'A','B','C');
    }
    //A B C
    //A -> C
    //A ->B  A->C B->C
    // A ->C  A->B  C->B A->C  B->A B->C  A->C
    /**
     *
     * @param n  盘子个数
     * @param pos1 盘子起始位置
     * @param pos2  盘子中转位置
     * @param pos3 盘子目标位置
     */
    public static void hanio(int n,char pos1,char pos2,char pos3){
        if(n==1){
            move(pos1,pos3);
            return ;
        }
        hanio(n-1,pos1,pos3,pos2);
        move(pos1,pos3);//只剩一个盘子,直接移动
        hanio(n-1,pos2,pos1,pos3);
    }
    public static void move(char m,char n){
        System.out.print(m+"->"+n+" ");
    }
}

结果

✔2.3.1青蛙跳台阶

一只青蛙可以一次跳 1 级台阶或一次跳 2 级台阶,例如:

跳上第一级台阶只有一种跳法:直接跳 1 级即可.

跳上两级台阶,有两种跳法: 每次跳 1 级,跳两次; 或者一次跳 2 级.

问要跳上第 n 级台阶有多少种跳法?

如图1至4个台阶的跳法种数

设台阶数为n,通过n=1,n=2,n=3,我们可以发现当n>2时,第一次跳的时候有两种不同的选择,一是第一次仅仅跳1级,此时跳法数目等于后面剩下的n-1个台阶的跳法数目,即f(n-1);当第一次选择跳2级台阶时,此时跳法的数目等于后面的剩下n-2级台阶的跳法数目,即为f(n-2)。所以求n个台阶的跳法为:f(n-1)+f(n-2)。

代码

public class Demo2 {
    public static void main(String[] args) {
        for(int i=1;i<10;i++){
            System.out.print(JumpFloor(i)+" ");
        }
    }
    public static int JumpFloor(int target){
        if(target==1){
            return 1;
        }else if(target==2){
            return 2;
        }else {
            return JumpFloor(target-1)+JumpFloor(target-2);
        }
    }
}

结果

3.总结

递归解决问题的思路:把一个复杂的问题层层转换为一个与原问题相似的规模较小的子问题来求解

递归算法三要素

  • 推导出递归的公式
  • 递归的终点。
  • 递归的方向必须走向终点,回归的起点也是终点

最后的话

各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!

相关文章
|
Java API Nacos
找不到`com.alibaba.nacos.api.utils.NetUtils`类
找不到`com.alibaba.nacos.api.utils.NetUtils`类
743 0
|
安全 数据安全/隐私保护
如何使用GPG 加密和解密文件
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情 大家好, 我是阿萨。又一个晴空万里的周一。祝大家本周都元气满满哦。 上次我们讲解了你知道PGP和GPG的区别 吗?有同学咨询如何使用 GPG 工具来加密文件。今天就来学习下如何安装 GPG 工具以及使用GPG 工具 的使用方法。 
960 0
如何使用GPG 加密和解密文件
|
9月前
|
弹性计算 运维 Cloud Native
云原生架构的崛起与未来展望
在数字化转型的浪潮中,云原生架构凭借其高效、灵活和可扩展的特性,正逐渐成为企业IT战略的核心。本文旨在探讨云原生架构的定义、关键特性、实施优势以及面临的挑战,同时展望未来的发展趋势。通过深入分析,我们期望为读者提供一个关于云原生架构全面而深入的视角,助力企业在云计算时代做出更明智的决策。
188 30
|
存储 测试技术 Go
Golang框架实战-KisFlow流式计算框架(2)-项目构建/基础模块-(上)
KisFlow项目源码位于&lt;https://github.com/aceld/kis-flow,初始阶段涉及项目构建和基础模块定义。首先在GitHub创建仓库,克隆到本地。项目目录包括`common/`, `example/`, `function/`, `conn/`, `config/`, `flow/`, 和 `kis/`。`go.mod`用于包管理,`KisLogger`接口定义了日志功能,提供不同级别的日志方法。默认日志对象`kisDefaultLogger`打印到标准输出。
765 74
Golang框架实战-KisFlow流式计算框架(2)-项目构建/基础模块-(上)
|
存储 Linux 图形学
深度探索Linux操作系统 —— Linux图形原理探讨1
深度探索Linux操作系统 —— Linux图形原理探讨
299 7
|
负载均衡 监控 安全
Istio:微服务治理的超级英雄,一键解锁你的服务网格超能力,让管理复杂变简单!
【8月更文挑战第31天】随着云原生技术的发展,微服务架构成为主流,但其复杂性与管理难题也随之增加。Istio作为开源服务网格平台,通过独特的数据平面和控制平面设计,实现了微服务通信的透明管理,简化了治理复杂度。本文将对比Istio与传统微服务管理方法,详细介绍Istio的架构及其工作原理,包括Envoy代理、服务发现、负载均衡、流量管理、安全认证以及监控等功能。Istio不仅简化了微服务治理,还提供了强大的流量控制和安全机制,使开发者能更高效地管理应用。
376 2
|
传感器 搜索推荐 人机交互
虚拟现实中的人机交互设计:探索未来交互的无限可能
【8月更文挑战第26天】虚拟现实中的人机交互设计是一项充满挑战与机遇的技术领域。随着技术的不断进步和应用场景的不断拓展,我们有理由相信未来VR人机交互将更加自然、直观和个性化。设计师需要不断探索和创新以应对各种技术挑战和用户需求变化,为用户带来更加丰富和愉悦的交互体验。
|
数据挖掘 UED
ERP系统的用户体验与界面设计:提升用户满意度与操作效率
【7月更文挑战第29天】 ERP系统的用户体验与界面设计:提升用户满意度与操作效率
1093 1
|
监控 安全 网络性能优化
|
缓存 数据可视化 安全
开发阿里云 RPA 机器人的技巧
在当今数字化时代,机器人流程自动化(RPA)技术正逐渐成为企业提高效率和优化业务流程的重要手段。阿里云 RPA 作为一种强大的工具,为开发高效的机器人提供了丰富的功能和支持。本文将分享一些开发阿里云 RPA 机器人的技巧,帮助您更好地利用该平台的能力。