递归
递归是一种解决问题的方法,通常涉及函数调用自身。我们使用递归,并不是因为它运行速度更快,而是因为它更利于理解,代码也少。
能够像下面这样直接调用自身的方法或函数,是递归函数:
var recursiveFunction = function(someParam){
recursiveFunction(someParam);
};
能够像下面这样间接调用自身的函数,也是递归函数:
var recursiveFunction1 = function(someParam){
recursiveFunction2(someParam);
};
var recursiveFunction2 = function(someParam){
recursiveFunction1(someParam);
};
栈溢出错误
递归并不会无限地执行下去;浏览器会抛出错误,也就是所谓的栈溢出错误(stack overflow error)
。
每个浏览器都有自己的上限,可用以下代码测试:
var i = 0;
function recursiveFn () {
i++;
recursiveFn();
}
try {
recursiveFn();
} catch (ex) {
console.log('i = ' + i + ' error: ' + ex);
}
计算斐波那契数列
递归方法
n = (n-1) + (n-2)
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …
function fibonacci(num){
if (num < 2) {
return num;
}
return fibonacci(num - 1) + fibonacci(num - 2);
}
上面这个函数的问题在于它的执行效率非常低,有太多的值在递归调用中被重新计算,如果编译器可以将已经计算的值记录下来,函数的执行效率就不会如此差。我们可以使用动态规划的技巧来设计一个效率更高的算法。
假如需要找出第六个位置的斐波那契数,如下所示,仅仅是第三个斐波那契数就计算了三次,求值越大,计算的次数会变得更多:
动态规划
使用递归去解决问题虽然简洁,但效率不高,尽管写出来的程序简洁,但是执行效率低下。许多使用递归去解决的编程问题,可以重写为使用动态规划的技巧去解决。动态规划方案通常会使用一个数组来建立一张表,用于存放计算过程中的值,可以避免重新计算。
function dynFib(n) {
if (n < 2) {
return n;
}
else {
var val = [0,1]; // 初始化 作为保存中间结果的数组
for (var i = 2; i <= n; ++i) {
val[i] = val[i-1