递归
一、概念:自己调用自己
案例:需求:算5!
方法一:使用for循环
int result=1;
for (int i = 1; i <=5; i++) {
result=result*i;
}
System.out.println(result);
}
方法二:使用递归
public static int fun(int num) {
if (num ==1) {
return 1;
}else {
return num*fun(num-1);
}
}
弊端:递归不能调用次数过多,否则栈内存溢出。
好处:不用知道循环次数
问题:
构造方法不能使用递归调用。递归调用可以有返回值也可以没有返回值。
递归练习(一)
需求:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名
分析:
1. 获取到该文件夹路径下的所有的文件和文件夹,存储在File数组中
2. 遍历数组,对每一个文件或文件夹做判断。
3. 如果是文件,并且后缀是.java就打印
4. 如果是文件夹就递归调用
public class Demo1_Test {
public static void main(String[] args) {
File dir =getDir();
printJavaFile(dir);
}
public static File getDir() {
Scanner scanner =new Scanner(System.in);
System.out.println("请输入一个文件夹路径:");
while (true) {
String line =scanner.nextLine();
File dir =new File(line);
if (!dir.exists()) {
System.out.println("您录入的文件夹路径不存在,请重新录入");
}else if (dir.isFile()) {
System.out.println("您录入的是文件夹路径,请重新录入文件路径");
}else {
return dir;
}
}
}
//获取文件夹路径下的所有.java文件
//返回值类型void
//参数列表File dir
public static void printJavaFile(File dir) {
//获取到该文件路径下的所有的文件和文件夹,存储在File数组中
File[] subFiles =dir.listFiles();
//遍历数组,对每一个文件或文件夹做判断
for (File subFile :subFiles) {
//如果是文件,并且后缀名是.java的就打印
if (subFile.isFile()&&subFile.getName().endsWith(".java")) {
System.out.println(subFile);
//如果是文件夹,就递归调用
}else if (subFile.isDirectory()) {
printJavaFile(subFile);
}
}
}
}
效果如下:
递归练习(二)
需求:从键盘接收一个文件夹路径,统计该文件夹大小
分析:
从键盘接受一个文件夹路径
- 创建键盘录入对象
- 定义一个无线循环
- 将键盘录入的结果存储并封装成File对象
- 对File对象判断
- 将文件夹路径对象返回
统计该文件夹大小
- 定义一个求和变量
- 获取该文件夹下所有的文件和文件夹ListFiles();
- 对数组进行遍历
- 判断是文件就计算大小并累加
- 判断是文件夹,递归调用
public class Demo2_Test {
public static void main(String[] args) {
//File dir =new File("d:\\day15");
//System.out.println(dir.length());//直接获取文件夹的结果是0
File dir =getFile();
System.out.println(getFileLength(dir));
}
public static File getFile() {
Scanner scanner =new Scanner(System.in);
System.out.println("请输入一个文件夹路径");
while (true) {
String line =scanner.nextLine();
File dir = new File(line);
//对File对象进行判断
if (!dir.exists()) {
System.out.println("文件路径不存在!");
}else if(dir.isFile()) {
System.out.println("这是文件夹路径,请输入文件路径");
}
else {
//将文件夹路径对象返回
return dir;
}
}
}
//统计文件大小。返回值类型long,参数列表File dir
public static long getFileLength(File dir) {
//定义一个求和变量
long len =0;
//获取该文件夹下所有的文件和文件夹ListFiles
File [] subFiles =dir.listFiles();
//遍历数组
for (File subFile : subFiles) {
//判断如果是文件就计算大小并累加
if (subFile.isFile()) {
len +=subFile.length();
}else {
//如果是文件夹的话就在继续递归调用
len+=getFileLength(subFile);
}
}
return len;
}
}
效果如下:
递归练习(三)
需求:从控制台输入路径,删除该文件夹
public class Demo3_Test {
public static void main(String[] args) {
File file =getFile();
deleteFile(file);
}
//删除文件夹
public static void deleteFile(File dir) {
File[]subFiles=dir.listFiles();
//遍历数组
for (File subFile : subFiles) {
if (subFile.isFile()) {
//如果是文件夹就直接删除
subFile.delete();
}else {
deleteFile(subFile);
}
}
//循环结束后,把空文件夹删掉
dir.delete();
}
public static File getFile() {
Scanner scanner =new Scanner(System.in);
System.out.println("请输入一个文件路径名称");
while (true) {
String line =scanner.nextLine();
File dir =new File(line);
if (!dir.exists()) {
System.out.println("文件路径不存在或输入有误");
}else if(dir.isFile()){
System.out.println("这是文件夹路径,请输入文件路径");
}else {
return dir;
}
}
}
}
递归练习(四)
需求:从键盘接收两个文件夹路径,把其中一个文件夹中(包含内容)拷贝到另一个文件夹中
分析:
- 在目标文件夹中创建原文件夹
- 获取原文件夹中所有的文件和文件夹,存储在File数组中
- 遍历数组
- 如果是文件就用io流读写
- 如果是文件夹就递归调用
public class Demo4_Test {
public static void main(String[] args) throws IOException {
File src =getFile();
File desc =getFile();
if (src.equals(desc)) {
System.out.println("目标文件夹是原文件夹的子文件夹");
}else {
copy(src,desc);
}
}
//把其中一个文件夹中(包含内容)拷贝到另一个文件夹中
//返回值类型void
//参数列表File src,File desc
public static void copy(File src, File desc) throws IOException {
//在目标文件夹中创建原文件夹
File newDir =new File(desc,src.getName());
newDir.mkdir();
//获取原文件夹中所有的文件和文件夹,存储在File数组中
File[] subFiles =src.listFiles();
for (File subFile : subFiles) {
//如果是文件用io读写
if (subFile.isFile()) {
BufferedInputStream bis =new BufferedInputStream(new FileInputStream(subFile));
BufferedOutputStream bos =
new BufferedOutputStream(new FileOutputStream(new File(newDir,subFile.getName())));
int b;
while ((b=bis.read())!=-1) {
bos.write(b);
}
bis.close();
bos.close();
}else {
copy(subFile, newDir);
}
}
}
public static File getFile() {
Scanner scanner =new Scanner(System.in);
System.out.println("请输入一个文件路径");
while (true) {
String line =scanner.nextLine();
File dir =new File(line);
if (!dir.exists()) {
System.out.println("文件路径不存在");
}
else if (dir.isFile()) {
System.out.println("这是文件夹路径,请输入文件路径");
}else {
return dir;
}
}
}
}
递归练习(五)
需求:按层级打印,从键盘接收一个文件夹路径,把文件夹中的所有文件以及文件夹的名字按层级打印, 例如:
aaa是文件夹,里面有bbb.txt,ccc.txt,ddd.txt这些文件,有eee这样的文件夹,eee中有fff.txt和ggg.txt,打印出层级来
分析:
1. 获取所有文件和文件夹,返回的File数组
2. 遍历数组
3. 无论是文件还是文件夹,都需要直接打印
4. 如果是文件夹,递归调用
public class Demo5_Test {
public static void main(String[] args) {
File dir =getFile();
printLev(dir,0);
}
public static void printLev(File dir ,int level) {
//把文件夹中的文件和文件夹放入File数组中
File[] subFiles =dir.listFiles();
for (File subFile : subFiles) {
for (int i = 0; i <=level; i++) {
System.out.print("\t");
}
//不论是文件还是文件夹,都需要直接打印
System.out.println(subFile);
//如果是文件夹,递归调用
if (subFile.isDirectory()) {
printLev(subFile,level+1);
//改变level++或者++level结果均不同
}
}
}
public static File getFile(){
Scanner scanner =new Scanner(System.in);
System.out.println("请输入一个文件路径");
while (true) {
String line =scanner.nextLine();
File dir =new File(line);
if (!dir.exists()) {
System.out.println("输入的文件路径不存在");
}else if (dir.isFile()) {
System.out.println("请输入一个文件路径");
}else {
return dir;
}
}
}
}
考虑该类问题一定要考虑进栈出栈问题
递归练习(六)
需求:斐波那契数列
用数列的意思就是前面两项相加等于当前项。an =an+1+an-2
public static int fun(int num) {
if (num ==1 || num==2) {
return 1;
}else {
return fun(num-2)+fun(num-1);
}
}
}
约瑟夫环
需求:寻找幸运数字
public class Demo8_Test {
public static void main(String[] args) {
System.out.println(getLucklyNum(8));
}
public static int getLucklyNum(int num) {
ArrayList<Integer> list =new ArrayList<Integer>(); //创建集合存储1到num的对象
for (int i = 0; i < num; i++) {
list.add(i);
}
int count =1; //用于数数,只要是3的倍数就循环
for (int i = 0;list.size()!=1; i++) { //只要集合中人数超过1,就不断地删除
if (i == list.size()) {
i=0; //重新归零
}
if (count%3==0) { //如果是3的倍数就删除
list.remove(i--);
}
count++;
}
return list.get(0);
}
}