JavaScript 递归

397 阅读2分钟

递归,可以通俗地理解为:无限套娃。

我们都听过:“从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,讲的是,从前有座山,山里……”

这个就是一种递归,先执行自己的部分,然后再调用自己,进行无限循环

1. 定义

在《学习 JavaScript 数据结构与算法(第三版)》书中,对递归的定义为:

递归是一种解决问题的方法,它从解决问题的各个小部分开始,直到解决最初的大问题。递归通常涉及函数调用自身

形式一

function recursiveFunction(someParam) {
  recursiveFunction(someParam);
}

形式二

function recursiveFunction1(someParam) {
  recursiveFunction2(someParam);
}
function recursiveFunction2(someParam) {
  recursiveFunction1(someParam);
}

上述两种形式的递归,都没有基线条件,及没有停止点,会一直递归下去,造成栈溢出的现象。故,一般在使用递归的时候,都会进行条件判断,满足条件之后就跳出递归,避免一直死循环下去。

2. 使用

阶乘

迭代阶乘

function factorialIterative(number) {
  if (number < 0) return undefined;
  let total = 1;
  for (let n = number; n > 1; n--) {
    total = total * n;
  }
  return total;
}

递归阶乘

function factorial(n) {
  if (n === 1 || n === 0) {
    return 1;
  }
  return n * factorial(n - 1); // 通过再次调用自身,进行计算
}

每当一个函数被一个算法调用时,该函数会进入调用栈的顶部

栈调用情况

如果没有添加用以停止函数递归的调用的基线条件,当出现栈溢出错误时,浏览器会抛出错误

斐波那契数

由 0、1、1、2、3、5、8、13、21、34 等数组成的序列。数 2 由 1 + 1 得到,数 3 由 1 + 2 得到,数 5 由 2 + 3 得到,以此类推。斐波那契数列的定义:

  • 位置 0 的斐波那契数是零
  • 1 和 2 的斐波那契数是 1
  • n(此处 n > 2)的斐波那契数是(n - 1)的斐波那契数加上(n - 2)的斐波那契数

迭代求斐波那契数

function fibonacciIterative(n) {
  if (n < 1) return 0;
  if (n <= 2) return 1;
  let fibNMinus2 = 0;
  let fibNMinus1 = 1;
  let fibN = n;
  for (let i = 2; i <= n; i++) {
    fibN = fibNMinus1 + fibNMinus2;
    fibNMinus2 = fibNMinus1;
    fibNMinus1 = fibN;
  }
  return fibN;
}

递归求斐波那契数

function fibonacci(n) {
  if (n < 1) return 0;
  if (n <= 20) return 1;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

记忆化

function fibonacciMemoization(n) {
  const memo = [0, 1]; //第1,2个数已经写好了
  const fibonacci = (n) => {
    if (memo[n] != null) return memo[n]; //如果是第1,2个数,直接返回本身
    return (memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)); //存缓存
  };
  return fibonacci(n);
}

3. 速度

从测试上看,迭代的速度会比递归的快很多。

使用递归的好处:

  • 代码量少
  • 结构清晰,易懂

递归主要是调用函数,需要不断地进行进栈出栈,所以消耗时间较多

具体可查看:《递归为什么那么慢?递归的改进算法》