function factorial(n)factorial(5) // 120
上面**是乙個階乘函式,計算n
的階乘,最多需要儲存n
個呼叫記錄,複雜度 o(n) 。
如果改寫成尾遞迴,只保留乙個呼叫記錄,複雜度 o(1) 。
函式呼叫自身,稱為遞迴。如果尾呼叫自身,就稱為尾遞迴。
遞迴非常耗費記憶體,因為需要同時儲存成千上百個呼叫幀,很容易發生「棧溢位」錯誤(stack overflow)。但對於尾遞迴來說,由於只存在乙個呼叫幀,所以永遠不會發生「棧溢位」錯誤
function factorial(n, total)factorial(5, 1) // 120
還有乙個比較著名的例子,就是計算 fibonacci 數列,也能充分說明尾遞迴優化的重要性。
非尾遞迴的 fibonacci 數列實現如下。
function fibonacci (n) ;return fibonacci(n - 1) + fibonacci(n - 2);}
fibonacci(10) // 89
fibonacci(100) // 堆疊溢位
fibonacci(500) // 堆疊溢位
尾遞迴優化過的 fibonacci 數列實現如下。
unction fibonacci2 (n , ac1 = 1 , ac2 = 1) ;return fibonacci2 (n - 1, ac2, ac1 + ac2);}
fibonacci2(100) // 573147844013817200000
fibonacci2(1000) // 7.0330367711422765e+208
fibonacci2(10000) // infinity
由此可見,「尾呼叫優化」對遞迴操作意義重大,所以一些函式式程式語言將其寫入了語言規格。es6 是如此,第一次明確規定,所有 ecmascript 的實現,都必須部署「尾呼叫優化」。這就是說,es6 中只要使用尾遞迴,就不會發生棧溢位,相對節省記憶體。
遞迴函式的改寫
function factorial(n, total = 1)factorial(5) // 120
普通遞迴與尾遞迴
遞迴就是函式直接或間接呼叫自身。遞迴函式設計時明確三點,一是明確遞迴邊界條件 二是繼續執行遞迴,三是遞迴返回。當不滿足遞迴邊界條件時,遞迴前進,也即繼續執行遞迴。當滿足邊界條件時,遞迴返回。1 資料格式就是遞迴形式,如fibonacci函式等 2 資料結構以遞迴定義,如二叉樹,圖等 3 問題解法是以...
python遞迴 Python 與尾遞迴優化
有很多時候,使用遞迴的方式寫 要比迭代更直觀一些,以下面的階乘為例 def factorial n if n 0 return 1 return factorial n 1 n 但是這個函式呼叫,如果展開,會變成如下的形式 factorial 4 factorial 3 4 factorial 2 ...
遞迴 遞迴的優化
遞迴演算法在工作或者各種資料結構中使用比較頻繁,遞迴演算法的簡化常見有自頂向下還有備忘錄法 自頂向下 t n t1 n t2 n t3 n 25c t1 n r11p1 r12p2 r13p3 r14p4 r15p5 r16p6 r17p7 r18p8 r19p9 t1 n 1 x tau1 t1 ...