这是一道常规的面试题目,以此题目来说明递归可能会增加计算的次数,造成性能浪费。可以把递归转换成循环的方式对递归算法进行优化。
常用来考递归的思考方式,因为递归看起来比循环高那么一点点,可实际上循环才是这道题的最优解法。递归的写法模式是前面写退出条件,后面写递归调用。
递归写法
public static int FibonacciRecursive(int n)
{
if (n < 1)
{
throw new Exception("参数必须大于0");
}
if (n == 1 || n == 2)
{
return 1;
}
return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
}
循环写法
public static int FibonacciLoop(int n)
{
if (n < 1)
{
throw new Exception("参数必须大于0");
}
if (n == 1 || n == 2)
{
return 1;
}
var x = 1;
var y = 1;
var result = 0;
for (var i = 3; i <= n; i++)
{
result = x + y;
x = y;
y = result;
}
return result;
}
Fibonacci(30) = 832040
递归调用没有记录中间计算的FibonacciXXX(n)结果,每一次都要重新计算,重复计算造成性能浪费。
写递归算法的时候,有意识地尝试转换成循环,如有性能优化就进行优化。
有人会将循环写法写成下面形式:
public static int FibonacciLoop(int n)
{
if (n < 1)
{
throw new Exception("参数必须大于0");
}
var x = 1;
var y = 1;
var result = 1;
for (var i = 3; i <= n; i++)
{
result = x + y;
x = y;
y = result;
}
return result;
}
代码量的确比原来的代码少了,但是可读性也是降低了。
更有甚者,优化成下面形式:
public static int FibonacciLoop(int n)
{
if (n < 1)
{
throw new Exception("参数必须大于0");
}
var x = 1;
var y = 1;
for (var i = 3; i <= n; i++)
{
y = x + y;
x = y - x;
}
return y;
}
这个代码更难理解了,应避免这样的过度优化代码。