主页
  • 主页
  • 分类
  • 热文
  • 教程
  • 面试
  • 标签
Java

Java 基础

Java 主页
Java 概述
Java 历史
Java 功能
Java 与 C++
Java JVM(Java虚拟机)
Java JDK、JRE 和 JVM
Java Hello World 程序
Java 环境设置
Java 基本语法
Java 变量类型
Java 数据类型
Java 类型转换
Java Unicode 系统
Java 基本运算符
Java 注释
Java 用户输入
Java 日期和时间

Java 控制语句

Java 循环控制
Java 决策结构
Java if-else 语句
Java switch 语句
Java for 循环
Java for each 循环
Java while 循环
Java do...while 循环
Java break 语句
Java continue 语句

Java 面向对象编程

Java OOP概念
Java 类和对象
Java 类属性
Java 类方法
Java 方法
Java 变量作用域
Java 构造函数
Java 访问修饰符
Java 继承
Java 聚合
Java 多态
Java 覆盖
Java 方法重载
Java 动态绑定
Java 静态绑定
Java 实例初始化块
Java 抽象
Java 封装
Java 接口
Java 包
Java 内部类
Java 静态类
Java 匿名类
Java 单例类
Java 包装类
Java 枚举类
Java 枚举构造函数
Java 枚举字符串

Java 内置类

Java 数字
Java 布尔值
Java 字符
Java 数组
Java 数学类

Java 文件处理

Java 文件
Java 创建文件
Java 写入文件
Java 读取文件
Java 删除文件
Java 目录操作
Java I/O流

Java 错误和异常

Java 异常
Java Try Catch
Java try-with-resources
Java 多个 Catch
Java 嵌套 try
Java finally
Java 抛出异常
Java 异常传播
Java 内置异常
Java 自定义异常

Java 多线程

Java 多线程
Java 线程生命周期
Java 创建线程
Java 启动线程
Java 加入线程
Java 命名线程
Java 线程调度
Java 线程池
Java 主线程
Java 线程优先级
Java 守护线程
Java 线程组
Java JVM 关闭

Java 同步

Java 线程同步
Java 块同步
Java 静态同步
Java 线程间通信
Java 线程死锁
Java 中断线程
Java 线程控制
Java 可重入锁

Java 网络

Java 网络编程
Java 套接字编程
Java URL 处理
Java URL 类
Java URLConnection 类
Java HttpURLConnection 类
Java Socket 类
Java 泛型

Java 集合

Java 集合框架
Java 集合接口

Java 接口

Java 列表接口
Java 队列接口
Java 映射接口
Java SortedMap 接口
Java 集合(Set)接口
Java SortedSet 接口

Java 数据结构

Java 数据结构
Java 枚举接口

Java 集合算法

Java 迭代器
Java 比较器
Java Comparable 接口

Java 高级

Java 命令行参数
Java Lambda 表达式
Java 发送电子邮件
Java 小应用程序
Java Javadoc
Java 自动装箱和拆箱
Java mismatch() 方法
Java REPL
Java 多版本发布 JAR
Java 私有接口方法
Java 金刚石操作符
Java 多分辨率图像 API
Java 集合的工厂方法
Java 模块系统
Java Nashorn 引擎
Java Optional 类
Java 方法引用
Java 功能接口
Java 默认方法
Java Base64 工具类
Java Switch 表达式
Java Collectors.teeing() 方法
Java 基准测试
Java 文本块
Java 动态CDS
Java ZGC
Java NullPointerException
Java jpackage
Java 密封类
Java 记录
Java 隐藏类
Java instanceof
Java 紧凑数字格式化
Java 垃圾回收
Java JIT 编译器

Java 杂项

Java 递归
Java 正则表达式
Java 序列化
Java 字符串类
Java 进程 API
Java Stream API
Java @Deprecated 注释
Java CompletableFuture API
Java Streams
Java 日期时间 API

基础

Java 主页
Java 概述
Java 历史
Java 功能
Java 与 C++
Java JVM(Java虚拟机)
Java JDK、JRE 和 JVM
Java Hello World 程序
Java 环境设置
Java 基本语法
Java 变量类型
Java 数据类型
Java 类型转换
Java Unicode 系统
Java 基本运算符
Java 注释
Java 用户输入
Java 日期和时间

控制语句

Java 循环控制
Java 决策结构
Java if-else 语句
Java switch 语句
Java for 循环
Java for each 循环
Java while 循环
Java do...while 循环
Java break 语句
Java continue 语句

面向对象编程

Java OOP概念
Java 类和对象
Java 类属性
Java 类方法
Java 方法
Java 变量作用域
Java 构造函数
Java 访问修饰符
Java 继承
Java 聚合
Java 多态
Java 覆盖
Java 方法重载
Java 动态绑定
Java 静态绑定
Java 实例初始化块
Java 抽象
Java 封装
Java 接口
Java 包
Java 内部类
Java 静态类
Java 匿名类
Java 单例类
Java 包装类
Java 枚举类
Java 枚举构造函数
Java 枚举字符串

内置类

Java 数字
Java 布尔值
Java 字符
Java 数组
Java 数学类

文件处理

Java 文件
Java 创建文件
Java 写入文件
Java 读取文件
Java 删除文件
Java 目录操作
Java I/O流

错误和异常

Java 异常
Java Try Catch
Java try-with-resources
Java 多个 Catch
Java 嵌套 try
Java finally
Java 抛出异常
Java 异常传播
Java 内置异常
Java 自定义异常

多线程

Java 多线程
Java 线程生命周期
Java 创建线程
Java 启动线程
Java 加入线程
Java 命名线程
Java 线程调度
Java 线程池
Java 主线程
Java 线程优先级
Java 守护线程
Java 线程组
Java JVM 关闭

同步

Java 线程同步
Java 块同步
Java 静态同步
Java 线程间通信
Java 线程死锁
Java 中断线程
Java 线程控制
Java 可重入锁

网络

Java 网络编程
Java 套接字编程
Java URL 处理
Java URL 类
Java URLConnection 类
Java HttpURLConnection 类
Java Socket 类
Java 泛型

集合

Java 集合框架
Java 集合接口

接口

Java 列表接口
Java 队列接口
Java 映射接口
Java SortedMap 接口
Java 集合(Set)接口
Java SortedSet 接口

数据结构

Java 数据结构
Java 枚举接口

集合算法

Java 迭代器
Java 比较器
Java Comparable 接口

高级

Java 命令行参数
Java Lambda 表达式
Java 发送电子邮件
Java 小应用程序
Java Javadoc
Java 自动装箱和拆箱
Java mismatch() 方法
Java REPL
Java 多版本发布 JAR
Java 私有接口方法
Java 金刚石操作符
Java 多分辨率图像 API
Java 集合的工厂方法
Java 模块系统
Java Nashorn 引擎
Java Optional 类
Java 方法引用
Java 功能接口
Java 默认方法
Java Base64 工具类
Java Switch 表达式
Java Collectors.teeing() 方法
Java 基准测试
Java 文本块
Java 动态CDS
Java ZGC
Java NullPointerException
Java jpackage
Java 密封类
Java 记录
Java 隐藏类
Java instanceof
Java 紧凑数字格式化
Java 垃圾回收
Java JIT 编译器

杂项

Java 递归
Java 正则表达式
Java 序列化
Java 字符串类
Java 进程 API
Java Stream API
Java @Deprecated 注释
Java CompletableFuture API
Java Streams
Java 日期时间 API

Java 递归


上一章 下一章

递归是一种编程技巧,其中方法通过自我调用来执行必要的子操作。调用自身的函数称为递归函数。递归主要用于将大问题分解为较小的问题,然后递归地解决这些问题。递归技术使代码更具可读性和表达力。

示例

考虑以下情况:

// 递归方法
public int sum(int n){
   // 递归方法调用
   return n == 1 ? 1 : n + sum(n-1);
}

在这个例子中,我们使用递归来获取自然数的和,这可以表示为 n 加上 n-1 数字的和。通过递归,我们将 n-1 自然数的和的结果与 n 相加以得到所需的结果。

由于递归函数会调用自身,因此必须有一个基本条件来终止递归方法无限调用自身。如果没有基本条件或基本条件永远不会成立,则会导致栈溢出。在上面的例子中,我们有一个 n 为 1 的基本条件。

public int sum(int n){
   // 基本条件
   if(n == 1){
      return 1;
   }
   // 递归调用
   return n + sum(n-1);
}

如果我们用一个负整数值调用此函数,则会导致栈溢出错误。

Java 中的递归是如何工作的?

在 Java 中,变量、方法调用、引用存储在栈中,而对象则在堆中分配内存。每当一个方法被调用时,它的详细信息就会被推送到栈中,如参数的值、任何局部变量、计算等。在递归调用期间,每当一个方法调用自身时,其条目会被推送到栈中,直到基本条件终止流程。当基本条件为真且方法开始返回值时,子调用的结果会从栈中弹出,以此类推,直到所有方法条目的记录从栈中弹出。让我们通过一个例子来理解这一点。

示例

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      Tester tester = new Tester();
      int result = tester.sum(5);
      System.out.println("Sum: " + result);
   }

   public int sum(int n){
      System.out.println("Input: " + n);
      int result;
      // 基本条件
      if(n == 1){
         result = 1;
         System.out.println("Base condition fulfilled.");
      }else {
         // 递归调用
         result = n + sum(n-1);
      }
      System.out.println("Result: " + result);
      return result;
   }
}

输出

当我们编译并运行上述程序时,会产生以下结果:

Input: 5
Input: 4
Input: 3
Input: 2
Input: 1
Base condition fulfilled.
Result: 1
Result: 3
Result: 6
Result: 10
Result: 15
Sum: 15

在这个程序中,我们可以很容易地看到,在递归调用期间,初始输入值一直打印出来,直到基本条件满足为止,因为方法调用被推送到栈中。一旦基本条件满足,递归调用结束,方法结果从栈中弹出,正如输出所示。

Java 递归示例

1. 使用递归来计算阶乘

阶乘是一个数学表达式,代表以下公式:

n! = n * (n-1)! 这类问题是使用递归来解决的理想候选者。请考虑以下代码段:

fact(n) = n * fact(n-1)

这里,fact() 是一个返回给定自然数的阶乘的方法。在实现 fact() 之前,我们应该考虑基本条件,即:

1! = 1

现在让我们来看一个完整的使用递归计算阶乘的例子:

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      Tester tester = new Tester();
      // 调用递归方法以获取阶乘
      int result = tester.fact(5);
      System.out.println("Factorial: " + result);
   }
   // 递归方法
   public int fact(int n) {
      // 如果基本条件不成立,则进行递归调用
      return n == 1 ? 1: n * fact(n-1);
   }
}

输出

当我们编译并运行上述程序时,会产生以下结果:

Factorial: 120

2. 使用递归来计算斐波那契数列之和

斐波那契数列是一个非常重要且有趣的数学序列。它代表以下等式:

F(n) = F(n-1) + F(n-2)

在这里,我们可以说,斐波那契数代表其前一个数和前前一个数的和。斐波那契数列的形式为 0, 1, 1, 2, 3, 5 等等。

使用递归,我们可以轻松计算斐波那契数。请考虑以下代码段:

fibo(n) = fibo(n-1) + fibo(n-2)

这里,fibo() 是一个返回给定整数的斐波那契数的方法。在实现 fibo() 之前,我们应该考虑基本条件,即:

fibo(0) = 0; fibo(1) = 1;

现在让我们来看一个完整的使用递归计算斐波那契数的例子:

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      Tester tester = new Tester();
      int result = tester.fibo(5);
      System.out.println("Fibbonacci: " + result);
   }

   public int fibo(int n) {
      return n <= 1 ? n : fibo(n-1) + fibo(n-2);
   }
}

输出

当我们编译并运行上述程序时,会产生以下结果:

Fibbonacci: 5

在 Java 中使用递归的优点

  • 更清晰的代码:使用递归可以使代码易于理解并保持代码整洁。相对于使用多个 if 和循环条件,递归有助于以函数的方式编写代码。
  • 递归算法:对于某些问题,如树遍历、汉诺塔问题等,递归是最好的解决方案。
  • 减少时间复杂度:递归程序有助于减少在大数据集上的搜索所需的时间。

在 Java 中使用递归的缺点

  • 技术要求高:虽然递归是一种更干净的方法,但它需要高度的专业知识和对问题陈述及提议解决方案的理解。不正确实施的递归可能会引起性能问题,并且可能难以调试。
  • 占用大量内存空间:由于涉及多次函数调用和返回流,递归程序通常占用大量内存。
上一章 下一章
阅读号二维码

关注阅读号

联系二维码

联系我们

© 2024 Yoagoa. All rights reserved.

粤ICP备18007391号

站点地图