递归是一种应用非常广泛的算法(或者编程技巧)。递归求解问题的分解过程,去的过程叫“递”,回来的过程叫“归”
1. 递归
- 递归: 在一个方法内部对自身进行调用
递归需要满足三个条件
- 一个问题的解可以分解为几个子问题的解
- 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
- 存在递归终止条件
递归的缺陷
- 简单的程序是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多是速度要比循环慢的多,所以在使用递归时要慎重
2. 递归案例-计算1~N的和
- 分析: num的累和 = num + (num-1)的累和,所以可以把累和的操作定义成一个方法,递归调用
package recursion;
/**
* @Description
* @auther 宁宁小可爱
* @create 2020-01-14 9:21
*/
public class sum_1_N {
public static void main(String[] args) {
// 计算1~num的和 使用递归
int num = 5;
// 调用求和方法
int sum = getSum(num);
System.out.println(sum);
}
public static int getSum(int num){
// 如果num为1 那么返回1
if (num == 1){
return 1;
// 否则返回num + (num-1)的累计和 递归调用
}else{
return num+getSum(num-1);
}
}
}
代码执行图解
小贴士:递归一定要有条件限定,保证递归能够停止下来,次数不要太多,否则会发生栈内存溢出
3. 递归案例-求阶乘
- 阶乘: 所有小于及等于该数的正整数的积
n的阶乘:n! = n * (n‐1) *...* 3 * 2 * 1
- 分析: 这与累和类似,只不过换成了乘法运算,需要注意阶乘值符合int类型的范围
- 推理得出:
n! = n * (n - 1)!
package recursion;
/**
* @Description
* @auther 宁宁小可爱
* @create 2020-01-14 9:31
*/
public class Factorial {
public static void main(String[] args) {
int n = 3;
int value = getValue(n);
System.out.println("阶乘为: "+value);
}
public static int getValue(int n){
// 1的阶乘为1
if (n == 1){
return 1;
}
// n 不为1 方法返回 n! = n*(n-1)!
// 递归调用getValue方法
return n * getValue(n - 1);
}
}
4. 递归案例-打印多级目录
- 分析: 多级目录的打印,就是当目录的嵌套。遍历之前,无从知道到底有多少级目录,所以我们还是要使用递归实现
package recursion;
import java.io.File;
/**
* @Description
* @auther 宁宁小可爱
* @create 2020-01-14 9:39
*/
public class TraversalFile {
public static void main(String[] args) {
File file = new File("D:\\softWare-all");
printDir(file);
}
public static void printDir(File dir){
// 获取子文件和目录
File[] files = dir.listFiles();
/*
* 判断: 是文件时,打印绝对路径
* 是目录时,继续调用打印目录方法,形成递归
* */
for (File file : files) {
if (file.isFile()){
System.out.println("文件名: "+file.getAbsolutePath());
}else {
System.out.println("目录: "+file.getAbsolutePath());
printDir(file);
}
}
}
}
5. 递归案例-文件搜索
搜索D:\\softWare-all
目录下所有的.java
文件
分析:
- 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录
- 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件
package recursion;
import java.io.File;
/**
* @Description
* @auther 宁宁小可爱
* @create 2020-01-14 9:39
*/
public class TraversalFile {
public static void main(String[] args) {
File file = new File("D:\\softWare-all");
printDir(file);
}
public static void printDir(File dir){
// 获取子文件和目录
File[] files = dir.listFiles();
/*
* 判断: 是文件时,打印绝对路径
* 是目录时,继续调用打印目录方法,形成递归
* */
for (File file : files) {
if (file.isFile()){
/* 如果是.java文件 那么判断文件名字输出绝对路径*/
if (file.getName().endsWith(".java")){
System.out.println("文件名: "+file.getAbsolutePath());
}
}else {
printDir(file);
}
}
}
}
5.1 文件过滤器优化
java.io.FileFilter
是一个接口,是File
的过滤器。 该接口的对象可以传递给File
类的listFiles(FileFilter)
作为参数, 接口中只有一个方法boolean accept(File pathname):
测试pathname
是否应该包含在当前File
目录中,符合则返回true
分析:
- 接口作为参数,需要传递子类对象,重写其中方法。我们选择匿名内部类方式,比较简单
accept
方法,参数为File
,表示当前File
下所有的子文件和子目录。保留住则返回true
,过滤掉则返回false
,要么是java文件,要么是目录,继续遍历- 通过过滤器的作用,
listFiles(FileFilter)
返回的数组元素中,子文件对象都是符合条件的,可以直接打印。
package recursion;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.io.File;
/**
* @Description
* @auther 宁宁小可爱
* @create 2020-01-14 9:55
*/
public class FileFilter {
public static void main(String[] args) {
File file = new File("D:\\softWare-all");
printDir(file);
}
public static void printDir(File dir){
// 匿名内部类 创建过滤器子类对象
File[] files = dir.listFiles(new java.io.FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java") || pathname.isDirectory();
}
});
// 循环打印
for (File file : files) {
if (file.isFile()){
System.out.println("文件名: "+file.getAbsolutePath());
}else {
printDir(file);
}
}
}
}
5.2 lambda优化
- 分析: :
FileFilter
是只有一个方法的接口,因此可以用lambda表达式简写 - lambda表达式如果不太理解请参考我这篇文章: lambda表达式传送门
package recursion;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.io.File;
/**
* @Description
* @auther 宁宁小可爱
* @create 2020-01-14 9:55
*/
public class FileFilter {
public static void main(String[] args) {
File file = new File("D:\\softWare-all");
printDir(file);
}
public static void printDir(File dir){
// 使用lambda表达式优化 只有一个参数省略小括号 return一个语句 省略大括号和return关键字
File[] files = dir.listFiles(pathname -> pathname.getName().endsWith(".java") || pathname.isDirectory());
// 循环打印
for (File file : files) {
if (file.isFile()){
System.out.println("文件名: "+file.getAbsolutePath());
}else {
printDir(file);
}
}
}
}