一文帶你搞懂Java中的遞歸
概述
遞歸:指在當(dāng)前方法內(nèi)調(diào)用自己的這種現(xiàn)象。
遞歸的分類:
- 遞歸分為兩種,直接遞歸和間接遞歸。
- 直接遞歸稱為方法自身調(diào)用自己。
- 間接遞歸可以A方法調(diào)用B方法,B方法調(diào)用C方法,C方法調(diào)用A方法。
注意事項(xiàng):
- 遞歸一定要有條件限定,保證遞歸能夠停止下來,否則會發(fā)生棧內(nèi)存溢出。
- 在遞歸中雖然有限定條件,但是遞歸次數(shù)不能太多。否則也會發(fā)生棧內(nèi)存溢出。
- 構(gòu)造方法,禁止遞歸
public class Demo01DiGui {
public static void main(String[] args) {
// a();
b(1);
}
/*
* 3.構(gòu)造方法,禁止遞歸
* 編譯報(bào)錯(cuò):構(gòu)造方法是創(chuàng)建對象使用的,不能讓對象一直創(chuàng)建下去
*/
public Demo01DiGui() {
//Demo01DiGui();
}
/*
* 2.在遞歸中雖然有限定條件,但是遞歸次數(shù)不能太多。否則也會發(fā)生棧內(nèi)存溢出。
* 4993
* Exception in thread "main" java.lang.StackOverflowError
*/
private static void b(int i) {
System.out.println(i);
//添加一個(gè)遞歸結(jié)束的條件,i==5000的時(shí)候結(jié)束
if(i==5000){
return;//結(jié)束方法
}
b(++i);
}
/*
* 1.遞歸一定要有條件限定,保證遞歸能夠停止下來,否則會發(fā)生棧內(nèi)存溢出。 Exception in thread "main"
* java.lang.StackOverflowError
*/
private static void a() {
System.out.println("a方法");
a();
}
}遞歸累加求和
計(jì)算1 ~ n的和
分析:num的累和 = num + (num-1)的累和,所以可以把累和的操作定義成一個(gè)方法,遞歸調(diào)用。 實(shí)現(xiàn)代碼:
public class DiGuiDemo {
public static void main(String[] args) {
//計(jì)算1~num的和,使用遞歸完成
int num = 5;
// 調(diào)用求和的方法
int sum = getSum(num);
// 輸出結(jié)果
System.out.println(sum);
}
/*
通過遞歸算法實(shí)現(xiàn).
參數(shù)列表:int
返回值類型: int
*/
public static int getSum(int num) {
/*
num為1時(shí),方法返回1,
相當(dāng)于是方法的出口,num總有是1的情況
*/
if(num == 1){
return 1;
}
/*
num不為1時(shí),方法返回 num +(num-1)的累和
遞歸調(diào)用getSum方法
*/
return num + getSum(num-1);
}
}代碼執(zhí)行圖解

小貼士:遞歸一定要有條件限定,保證遞歸能夠停止下來,次數(shù)不要太多,否則會發(fā)生棧內(nèi)存溢出。
遞歸求階乘
階乘:所有小于及等于該數(shù)的正整數(shù)的積。
n的階乘:n! = n * (n-1) *...* 3 * 2 * 1
分析:這與累和類似,只不過換成了乘法運(yùn)算,學(xué)員可以自己練習(xí),需要注意階乘值符合int類型的范圍。
推理得出:n! = n * (n-1)!
代碼實(shí)現(xiàn):
public class DiGuiDemo {
//計(jì)算n的階乘,使用遞歸完成
public static void main(String[] args) {
int n = 3;
// 調(diào)用求階乘的方法
int value = getValue(n);
// 輸出結(jié)果
System.out.println("階乘為:"+ value);
}
/*
通過遞歸算法實(shí)現(xiàn).
參數(shù)列表:int
返回值類型: int
*/
public static int getValue(int n) {
// 1的階乘為1
if (n == 1) {
return 1;
}
/*
n不為1時(shí),方法返回 n! = n*(n-1)!
遞歸調(diào)用getValue方法
*/
return n * getValue(n - 1);
}
}遞歸打印多級目錄
分析:多級目錄的打印,就是當(dāng)目錄的嵌套。遍歷之前,無從知道到底有多少級目錄,所以我們還是要使用遞歸實(shí)現(xiàn)。
代碼實(shí)現(xiàn):
public class DiGuiDemo2 {
public static void main(String[] args) {
// 創(chuàng)建File對象
File dir = new File("D:\\aaa");
// 調(diào)用打印目錄方法
printDir(dir);
}
public static void printDir(File dir) {
// 獲取子文件和目錄
File[] files = dir.listFiles();
// 循環(huán)打印
/*
判斷:
當(dāng)是文件時(shí),打印絕對路徑.
當(dāng)是目錄時(shí),繼續(xù)調(diào)用打印目錄的方法,形成遞歸調(diào)用.
*/
for (File file : files) {
// 判斷
if (file.isFile()) {
// 是文件,輸出文件絕對路徑
System.out.println("文件名:"+ file.getAbsolutePath());
} else {
// 是目錄,輸出目錄絕對路徑
System.out.println("目錄:"+file.getAbsolutePath());
// 繼續(xù)遍歷,調(diào)用printDir,形成遞歸
printDir(file);
}
}
}
}綜合案例
文件搜索
搜索D:\aaa 目錄中的.java 文件。 分析:
- 目錄搜索,無法判斷多少級目錄,所以使用遞歸,遍歷所有目錄。
- 遍歷目錄時(shí),獲取的子文件,通過文件名稱,判斷是否符合條件。
代碼實(shí)現(xiàn):
public class DiGuiDemo3 {
public static void main(String[] args) {
// 創(chuàng)建File對象
File dir = new File("D:\\aaa");
// 調(diào)用打印目錄方法
printDir(dir);
}
public static void printDir(File dir) {
// 獲取子文件和目錄
File[] files = dir.listFiles();
// 循環(huán)打印
for (File file : files) {
if (file.isFile()) {
// 是文件,判斷文件名并輸出文件絕對路徑
if (file.getName().endsWith(".java")) {
System.out.println("文件名:" + file.getAbsolutePath());
}
} else {
// 是目錄,繼續(xù)遍歷,形成遞歸
printDir(file);
}
}
}
}文件過濾器優(yōu)化
java.io.FileFilter是一個(gè)接口,是File的過濾器。 該接口的對象可以傳遞給File類的listFiles(FileFilter) 作為參數(shù), 接口中只有一個(gè)方法。 boolean accept(File pathname) :測試pathname是否應(yīng)該包含在當(dāng)前File目錄中,符合則返回true。
分析:
1.接口作為參數(shù),需要傳遞子類對象,重寫其中方法。我們選擇匿名內(nèi)部類方式,比較簡單。
2.accept方法,參數(shù)為File,表示當(dāng)前File下所有的子文件和子目錄。保留住則返回true,過濾掉則返回false。保留規(guī)則:
- 要么是.java文件。
- 要么是目錄,用于繼續(xù)遍歷。
3.通過過濾器的作用,listFiles(FileFilter)返回的數(shù)組元素中,子文件對象都是符合條件的,可以直接打印。
代碼實(shí)現(xiàn):
public class DiGuiDemo4 {
public static void main(String[] args) {
File dir = new File("D:\\aaa");
printDir2(dir);
}
public static void printDir2(File dir) {
// 匿名內(nèi)部類方式,創(chuàng)建過濾器子類對象
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java")||pathname.isDirectory();
}
});
// 循環(huán)打印
for (File file : files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir2(file);
}
}
}
}Lambda優(yōu)化
分析:FileFilter是只有一個(gè)方法的接口,因此可以用lambda表達(dá)式簡寫。 lambda格式:
()->{ }
代碼實(shí)現(xiàn):
public static void printDir3(File dir) {
// lambda的改寫
File[] files = dir.listFiles(f ->{
return f.getName().endsWith(".java") || f.isDirectory();
});
// 循環(huán)打印
for (File file : files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir3(file);
}
}
}以上就是一文帶你搞懂Java中的遞歸的詳細(xì)內(nèi)容,更多關(guān)于Java遞歸的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring boot+mybatis+thymeleaf 實(shí)現(xiàn)登錄注冊增刪改查功能的示例代碼
這篇文章主要介紹了Spring boot+mybatis+thymeleaf 實(shí)現(xiàn)登錄注冊增刪改查功能的示例代碼,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
SpringBoot多環(huán)境切換的配置實(shí)現(xiàn)
在日常的開發(fā)中,一般都會分好幾種環(huán)境,本文就來介紹一下SpringBoot多環(huán)境切換的配置實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
java中map和對象互轉(zhuǎn)工具類的實(shí)現(xiàn)示例
這篇文章主要介紹了java中map和對象互轉(zhuǎn)工具類的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
SpringCloud Nacos配置中心管理超詳細(xì)講解
這篇文章主要介紹了Springcloud中的Nacos服務(wù)配置,本文以用戶微服務(wù)為例,進(jìn)行統(tǒng)一的配置,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
SpringBoot編譯target目錄下沒有resource下的文件踩坑記錄
這篇文章主要介紹了SpringBoot編譯target目錄下沒有resource下的文件踩坑記錄,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
淺談Spring Cloud Eureka 自我保護(hù)機(jī)制
這篇文章主要介紹了淺談Spring Cloud Eureka 自我保護(hù)機(jī)制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
Springboot3整合Mybatis-plus3.5.3報(bào)錯(cuò)問題解決
在日常學(xué)習(xí)springboot3相關(guān)的代碼時(shí),在使用 SpringBoot3 整合 MyBatisplus 時(shí)出現(xiàn)了一些問題,花了不少時(shí)間處理,這篇文章主要介紹了Springboot3整合Mybatis-plus3.5.3報(bào)錯(cuò)問題解決,需要的朋友可以參考下2023-11-11

