/**分析:上述**就是遞迴,通俗的講就是自己呼叫自己;* 遞迴計算n 的階乘
* @param n
* @return
*/public long test(long n)else
}/**
* 優化計算 n 的階乘,尾遞迴
* @param n
* @param result=1
* @return
*/public long newtest(long n,long result)else
}
在執行函式test時,他也呼叫了另外乙個函式,只不過這個函式的**和上乙個函式的**一模一樣!是不是很簡單
看一下機器層面的執行過程:
此時就需要引入棧幀的概念了:
1:棧幀將棧分割成n個記錄塊,每乙個記錄塊的大小是不一樣的;
2:這個記錄塊實際上是編譯器用來實現函式呼叫的資料結構,通俗來講就是用於活動記錄,他用於記錄每次函式呼叫所涉及的相關資訊的記錄單元;
3:棧幀也是乙個函式的執行環境,它包括函式的引數,函式的區域性變數函式,執行完之後要返回到**等等;
說到這裡貌似,大約,好像明白了棧幀原來是用於呼叫函式的,你每呼叫一次函式他就會形成乙個棧幀用於這個被呼叫函式的執行環境;
說到這,貌似懂了:上邊的test函式在執行時就是形成了乙個又乙個棧幀啊!
針對上邊的遞迴函式,我畫了一幅函式在棧中的執行示意圖;
棧是一種先進後出的資料結構!!!
分析:要求計算5的階乘;
1):呼叫test函式時傳入5,即首先在棧中劃出乙個記錄塊做為函式test(5)的執行環境;執行到最後結果為: 5 * test(4);
2):上乙個函式的返回值中呼叫函式test(4),因此繼續指向新的記錄塊,用於執行函式test(4);執行到最後結果為: 4 * test(3);
3):上乙個函式的返回值中呼叫函式test(3),因此繼續指向新的記錄塊,用於執行函式test(3);執行到最後結果為: 3 * test(2);
4):上乙個函式的返回值中呼叫函式test(2),因此繼續指向新的記錄塊,用於執行函式test(2);執行到最後結果為: 2 * test(1);
5):上乙個函式的返回值中呼叫函式test(1),因此繼續指向新的記錄塊,用於執行函式test(1);執行到最後test(1)=1;
此時進棧操作已經到達了遞迴終止的條件,為了計算出最後的test(5)的值需要執行出棧操作;
如上圖,我畫了一幅出棧示意圖;棧是先進後出的,所以最後進的要先出。
1):test(1)出棧,返回值為1;
2):棧幀test(2)接收test(1)返回值進行計算得出test(2) = 2 * 1 = 2;
3):test(2)出棧,棧幀test(3)接收test(2)返回值進行計算得出test(3) = 3 * 2 = 6;
4):test(3)出棧,棧幀test(4)接收test(3)返回值進行計算得出test(4) = 4 * 6 = 24;
5):test(4)出棧,棧幀test(5)接收test(4)返回值進行計算得出test(5) = 5 * 24 = 120;
6):test(5)出棧,返回值120,此時表示這一段程式已經執行完畢,計算得出5的階乘是120;
遞迴函式寫到這一步,貌似是已經完美了,但是你有沒有想過:每乙個函式test(n) = n * test (n-1)因此每乙個棧幀不僅需要儲存n值還要記錄下乙個棧幀的返回值,然後才能計算出來當前棧幀的結果,因此使用多個棧幀是不可避免的,計算5的階乘就使用了5個棧幀,那要是計算100的呢?10000的呢?。。。。這tm是不是有點始料未及了?棧的大小也是有限的,你就這麼用下去,他不給你溢位才怪。
上面的方法實際執**況
test(10) // 89尾遞迴優化只在嚴格模式下生效,那麼正常模式下,或者那些不支援該功能的環境中,有沒有辦法也使用尾遞迴優化呢?回答是可以的,就是自己實現尾遞迴優化。test(100) // 堆疊溢位
test()方法100就計算不出來了
newtest(100,1) // 573147844013817200000
newtest(1000,1) // 7.0330367711422765e+208
newtest(10000,1) // infinity
它的原理非常簡單。尾遞迴之所以需要優化,原因是呼叫棧太多,造成溢位,那麼只要減少呼叫棧,就不會溢位。怎麼做可以減少呼叫棧呢?就是採用「迴圈」換掉「遞迴」。
/*** 迴圈
綜上,遞迴演算法涉及到棧,而棧又是先進後出的原則,因此大量的遞迴很容易造成棧的記憶體溢位,因此需要優化。
方向是:遞迴》尾遞迴》迴圈
所以你不懂遞迴 還是別玩
演算法優化 遞迴演算法的優化策略
在處理演算法問題時候,用的非常多的一種策略就是遞迴演算法了。但是遞迴演算法雖然簡單有效,但是該演算法的演算法效果總是有點差強人意。本文主要講述從兩個方向優化遞迴演算法,希望本文能給讀者一些thinking。持續更新中 總結 示例 遞迴演算法我又把它稱為迭代演算法,因為大致思路都差不多 迭代 從已知推...
遞迴演算法的優化
經群裡高人點撥。決定還是先把演算法優化下試試。遞迴演算法本身效率就比較低下,由於其便捷性,還有本人思維狹隘性,目前還無法想出替代它的方法。只能從遞迴內部的邏輯運算和變數等開始著手。先在各個過程裡加上計時器,看 耗時最大。例如 timer new stopwatch timer.start times...
遞迴 遞迴演算法的非遞迴優化
一 遞迴 在方法內部呼叫自身方法的過程稱為遞迴,下面給出乙個遞迴方法的示例。class program 使用遞迴,實現求前n項和 public static int getsum int n int result getsum n 1 在方法體中呼叫方法本身 return result n 需要注意...