最近学习算法遇到一个很有意思的事情。
就是大家都知道递归用的不好是非常占内存而且耗时的。原因是因为递归函数是一层一层调用函数的。而在计算机中函数调用是通过栈(stack)这种数据结构实现,每当调用一层函数,栈就会加一层栈帧,每当函数返回,栈就会减少一层栈帧。所以递归用的不好可能会导致栈溢出。或者运行时间特别长。
优化递归的方法可以使用尾递归,事实上时间复杂度一样。尾递归主要是对栈内存空间优化,其实是由于对空间优化导致内存分配的工作减少产生。
而且可以把循环看成一种特殊的尾递归。
尾递归:在函数返回时只调用自身。尾递归在执行过程中不需要回溯。因为尾递归把结果包含在一部分参数中。故每次调用实际上都是对参数的一个更新过程。
//普通递归
public static long fib_1(int n){
if(n<=1){
return 1;
}
return fib_1(n-1)+fib_1(n-2);
}
//尾递归
public static long fib_2(int n){
return fib_iter(1,1,n);
}
public static long fib_iter(long a,long b,int n){
if(n<=0){
return a;
}
return fib_iter(b,a+b,n-1);
}
//for循环
public static long fib_3(int n){
if(n<=1){
return 1;
}else{
long fib=0;
long a=1;
long b=1;
for(int i=1;i<n;i++){
fib=a+b;
a=b;
b=fib;
}
return fib;
}
}
//测试
public static void main(String[] args) {
int NUM=40;
long startTime1=System.currentTimeMillis();
long m=fib_2(NUM);
long endTime1=System.currentTimeMillis();
System.out.println("fib_2:"+m);
System.out.println("Time:"+(endTime1-startTime1));
long startTime2=System.currentTimeMillis();
long k=fib_3(NUM);
long endTime2=System.currentTimeMillis();
System.out.println("fib_3:"+k);
System.out.println("Time:"+(endTime2-startTime2));
long startTime=System.currentTimeMillis();
long n=fib_1(NUM);
long endTime=System.currentTimeMillis();
System.out.println("fib_1:"+n);
System.out.println("Time:"+(endTime-startTime));
}
输出:
fib_2:165580141
Time:0
fib_3:165580141
Time:0
fib_1:165580141
Time:1556
可以看到for循环和尾递归还是非常快的。