這篇文章轉過來的:
function foo(data)
這裡的a(data)和b(data)都是函式呼叫,但是b(data)是函式返回前的最後執行的東西,所以也是所謂的尾位置。例如:
function foo1(data)
function foo2(data)
function foo3(data)
這種就不是尾呼叫,對於foo1,最後乙個動作是+1操作,並非是直接函式呼叫;對於foo3,是經過計算返回的結果,也不是尾呼叫。,foo2也不是尾呼叫
尾呼叫很重要的特性就是它可以不在呼叫棧上面新增乙個新的堆疊幀,而是更新它。
若乙個函式在尾位置呼叫本身(或是乙個尾呼叫本身的其他函式等),則稱這種情況為尾遞迴,是遞迴的一種特殊情形。而形式上只要是最後乙個return語句返回的是乙個完整函式,它就是尾遞迴。這裡注意:尾呼叫不一定是遞迴呼叫,但是尾遞迴一定是尾呼叫。
接下來通過斐波那契數列和階乘來進一步理解尾遞迴的含義。
斐波那契數列
常規的斐波那契數列演算法可能是這樣的:
int fib(int n)
return fib(n - 1) + fib(n - 2);
}
上面的這種遞迴計算最終的return操作是加法操作。所以不是尾遞迴。
如果用尾遞迴就是這樣的:
/**
計算第n位斐波那契數列的值
@param n 第n個數
@param acc1 第n個數
@param acc2 第n與第n+1個數的和
@return 返回斐波那契數列值
*/int tailfib(int n,int acc1,int acc2)
return tailfib(n-1,acc2,acc1 + acc2);
}
比如我們想計算第10位斐波那契數列的值,只需要fib(10,1,1)即可。
看一下測試效果,測試程式如下:
int main(int argc, const char * ar**)
計算結果如下:
計算結果:1134903170
花費時間--------5540692
計算結果:1134903170
花費時間--------4
program ended with exit code: 0
效率上,尾遞迴可謂碾壓普通遞迴
階乘
常規的計算階乘的方法可能是這樣的:
int fac(int n)
return fac(n-1) * n;
}
複雜度為o(n)
尾遞迴的演算法是這樣的:
int tailfac(int n,int sum)
return fac(n-1, n * sum);
}
複雜度為o(1)
測試一下效率,測試程式如下:
int main(int argc, const char * ar**)
測試結果:
計算結果:2004189184
花費時間--------31
計算結果:2004189184
花費時間--------2
尾遞迴效率比較高,但是個人覺得有尾遞迴演算法理解起來會比較困難,你需要標註一下每個傳入引數的作用,否則剛接觸不一定會用這個演算法。 while for 遞迴函式 哪個效率高
for var oldlist array 0,0,0,0,0 var narr array new array for var i 0 i oldlist.length i trace narr while while i oldlist.length 遞迴code var oldlist arr...
前置 為什麼比後置 效率高
前置 type operator 後置 const type operator int 為了編譯器區分前置和後置 c 規定字尾形式有乙個int型別引數 當函式被呼叫時,編譯器傳遞乙個0做為int引數的值給該函式。不這樣規定,無法區分,因為都僅以自身物件為入參。下面是乙個簡單的例子 class cin...
前置 為什麼比後置 效率高
為了編譯器區分前置和後置 c 規定字尾形式有乙個int型別引數 當函式被呼叫時,編譯器傳遞乙個0做為int引數的值給該函式。不這樣規定,無法區分,因為都僅以自身物件為入參。下面是乙個簡單的例子 class cint cint cint operator 前置的是沒有引數的,並且返回引用 const ...