递归下降分析程序的设计与实现_递归就是这么简单
維基百科給出了如下定義:
程序調用自身的編程技巧稱為遞歸.遞歸作為一種算法在程序設計語言中廣泛應用。
上面的說法略顯官方。簡而言之,遞歸就是自己調用自己,但是這個調用它是有一定條件的,比如:
子問題須與原始問題為同樣的事,且更為簡單。
調用自身的次數不能太多,否則會造成程序堆棧溢出。
必須設置遞歸邊界,也就是遞歸的結束條件,否則遞歸會無限循環直到程序堆棧溢出。
2、遞歸與循環的區別
遞歸
優點:代碼簡潔、清晰(需要你理解算法,否則會更暈)缺點:調用次數控制不好,容易造成堆棧溢出,此外,它的每次傳遞參數都是相當于在壓棧,每次返回結果都相當于出棧,這個過程是非常影響執行效率的。
循環
優點:邏輯簡單,速度快缺點:不能解決所有的問題,有些問題必須用遞歸實現。比如,著名的漢若塔問題,如果有誰可以用其他方式寫出來我服。
3、遞歸的使用場景
關于使用場景,我總結了一句話:調用次數較少且用循環實現極為惡心的時候,可以嘗試使用遞歸。
4、關于遞歸的幾個示例
?① 計算 int 數組的總和
public?class?Main?{????private?static?int?sum(int[]?arr,?int?z)?{
????????if?(z?==?arr.length)?{
????????????return?0;
????????}
????????int?x?=?sum(arr,?z?+?1);
????????int?res?=?arr[z]?+?x;
????????return?res;
????}
????public?static?void?main(String[]?args)?{
????????int?arr[]?=?{1,?2};
????????sum(arr,?0);
????}
}
這個示例最簡單,當然這里是為了方便說明問題,實際上用循環實現才是最好的。目的就是計算 arr 數組各元素的總和,看輸入參數,大家可以猜到返回結果是 3 。下面我說下看這類程序的小技巧。
首先,我們要找到程序的遞歸邊界,也就是遞歸結束的條件(這樣說也不準確,看具體的代碼實現,有時遞歸邊界確實是遞歸結束的條件,返回最終結果,但有時又是遞歸最后一層返回結果的條件,比如以下程序)。
當 z = 2 時,這層遞歸返回 0 ,也就是 x = 0、返回到 z = 1 的這層遞歸。
此時,res = arr[1] + 0 ,返回 res = 2 也就是 x = 2 給到 z = 0 的這層遞歸。
這時,res = arr[0] + 2 = 3 至此,遞歸結束,返回結果給調用方。
沒看懂的,請復制代碼 debug 一步一步的運行。一開始看反正我是被繞暈的。
② 計算 1 到 100 的總和
public?class?Main?{????static?int?i?=?1;
????public?static?void?show(int?sum)?{
????????sum?=?sum?+?i;?//業務代碼1
????????//遞歸頭
????????if?(i?==?10)?{
????????????System.out.println(sum);
????????????return;
????????}
????????i++;???//業務代碼2
????????show(sum);?//遞歸體
????}
????public?static?void?main(String[]?args)?{
????????int?sum?=?0;
????????show(sum);
????}
}
以上寫法的遞歸邊界,就屬于我上面說的,它就是遞歸結束的條件。它的返回結果就是遞歸的最終結果,而不是上一層的結果。
③ 斐波那契數列
public?class?Main?{????public?static?int?f(int?n)?throws?Exception?{
????????if(n==0){
???????????throw?new?Exception("參數錯誤!");
????????}
????????if?(n?==?1?||?n?==?2)?{
????????????return?1;
????????}?else?{
????????????return?f(n-1)+f(n-2);?//自己調用自己
????????}
?}
????public?static?void?main(String[]?args)?throws?Exception?{
????????for?(int?i?=?1;?i?<=10;?i++)?{
????????????System.out.print(f(i)+"?");
????????}
????}??
}
④ 計算文件夾大小
由于 File 類下length() (返回值類型為 long 型) 方法只能統計文件的大小,沒有方法直接統計文件夾的大小,需要使用遞歸的方法遍歷到所有的文件,并累加,最終計算出文件夾大小。
public?class?Main?{????public?static?void?main(String[]?args)?{
????????File?dir?=?getDir();
????????System.out.println(getFileLength(dir));
????????System.out.println("統計完成!");
????}
????public?static?File?getDir()?{
????????//1,創建鍵盤錄入對象
????????Scanner?sc?=?new?Scanner(System.in);
????????System.out.println("請輸入一個文件夾路徑:");
????????//2,定義一個無限循環
????????while(true)?{
????????????//3,將鍵盤錄入的結果存儲并封裝成File對象
????????????String?line?=?sc.nextLine();
????????????File?dir?=?new?File(line);
????????????//4,對File對象判斷
????????????if(!dir.exists())?{
????????????????System.out.println("您錄入的文件夾路徑不存在,請重新錄入:");
????????????}else?if(dir.isFile())?{
????????????????System.out.println("您錄入的是文件路徑,請重新錄入:");
????????????}else?{
????????????????//5,將文件夾路徑對象返回
????????????????return?dir;
????????????}
????????}????????
????}
????public?static?long?getFileLength(File?dir)?{
????????//1,定義一個求和變量
????????long?len?=?0;
????????//2,獲取該文件夾下所有的文件和文件夾listFiles();
????????File[]?subFiles?=?dir.listFiles();
????????//3,遍歷數組
????????if(subFiles?!=?null)?{
????????????for?(File?subFile?:?subFiles)?{
????????????????//4,判斷是文件就計算大小并累加
????????????????if(subFile.isFile())?{
????????????????????len?=?len?+?subFile.length();
????????????????????//5,判斷是文件夾,遞歸調用
????????????????}else?{
????????????????????len?=?len?+?getFileLength(subFile);
????????????????}
????????????}
????????}
????????return?len;
????}
}
總結:這篇主要是介紹下遞歸的定義、與循環的區別以及它的使用場景,最后提供了幾個代碼示例給大家研究,看不懂的請復制代碼,debug 一步一步運行理解。
參考鏈接:https://www.jianshu.com/p/edfc4e35f383
推薦閱讀:
1、java | 什么是動態代理
2、SpringBoot | 啟動原理
3、SpringBoot | 自動配置原理
總結
以上是生活随笔為你收集整理的递归下降分析程序的设计与实现_递归就是这么简单的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mysql:is not allowed
- 下一篇: Oracle查询忽略大小写的实现方法