普通递归中进行了大量重复计算,同时又容易栈溢出。与普通递归相比,由于尾递归的调用处于方法的最后,因此方法之前所积累下的各种状态对于递归调用结果已经没有任何意义,因此完全可以把本次方法中留在堆栈中的数据完全清除,把空间让给最后的递归调用。这样的优化便使得递归不会在调用堆栈上产生堆积,意味着即时是“无限”递归也不会让堆栈溢出。这便是尾递归的优势。
下面来看一下普通递归与尾递归的比较:
#pragma once #include <iostream> #include <ctime> #include <windows.h> using namespace std; int fabonaci(int n) { if (1 >= n) { return n; } return fabonaci(n - 1) + fabonaci(n - 2); } int fabonaciTail(int n,int acc1,int acc2) { if (n == 0) { return acc1; } return fabonaciTail(n - 1,acc2,acc1 + acc2); } int main() { DWORD tt; tt = GetTickCount(); cout<<fabonaci(35); cout<<" 时间:"<<GetTickCount() - tt<<endl; tt = GetTickCount(); cout<<fabonaciTail(35,0,1)<<"注意cout是从右往左入栈: "<<GetTickCount() - tt<<endl;//0 cout<<" 第二次的时间:"<<GetTickCount() - tt<<endl; system("pause"); return 0; }
用尾递归的计算过程是线性过程,只递归调用n次负责度为O(n),而普通递归则是树状调用,复杂度呈几何级数增长,复杂度为:O(2En)。
/*补充: cout是从右到左运算完再输出的吧,输出顺序是从左到右,参数入栈顺序就不一样了 1.cout参数入栈的顺序是从右到左...例如cin < <a < <b < <c;在栈中的位置如下:c-b-a.. <-(栈指针);但是输出是从栈指针的位置开始的,意思即这时的输出顺序仍然是abc....否则有违常理... 2.如果参数是有待计算的函数,例如...cin < <f(a) < <f(b) < <f(c) < <endl;这时我认为cin会先把函数放进栈中然后再计算..意思是这时栈顺序是f(c)-f(b)-f(a).. <-..但是这时候计算的顺序却是f(c)最先,f(b)次之,f(a)最后... 下面的程序验证了我说的: C/C++ code */ #include <iostream> using namespace std; int fa() { cout<<"fa()"<<endl; return 1; } int fb() { cout<<"fb()"<<endl; return 2; } int fc() { cout<<"fc()"<<endl; return 3; } int main() { cout<<fa()<<fb()<<fc()<<endl; return 0; } /* 输出:fc() fb() fa() 123 */
下面再用尾递归来解决链表长度求解的问题:
2.是否所有的递归都可转换为尾递归?