day12 JS语义分析该用迭代还是递归?

迭代:循环往复的过程
递归:信息的传递需要一去一回

function factorialIterative(number) {
  if (number < 0) return undefined;
  let total = 1;
  for (let n = number; n > 1; n--) {
    total = total * n;
  }
  return total;
}
console.log(factorialIterative(7)); // 5040

递归通常有两个基本元素一个是基本条件,另一个是递归本身。

function factorialRecursive(n) {
  // 基本条件
  if (n === 1 || n === 0) { 
    return 1;
  }
  // 递归调用
  return n * factorialRecursive(n - 1); 
}
console.log(factorialRecursive(7)); // 5040

上面这段代码在执行中如下。我们可以看到前面 7 步,都是递的过程。在碰到基本条件后,开始了归的过程。

递归中用到的分治
斐波那契(Fibonacci Sequence)数列对比下迭代和递归:
迭代:

function fibIterative(n) {
  if (n < 1) return 0;
  if (n <= 2) return 1; 
  let fibNMinus2 = 0;
  let fibNMinus1 = 1;
  let fibN = n;
  // n >= 2
  for (let i = 2; i <= n; i++) { 
    // f(n-1) + f(n-2)
    fibN = fibNMinus1 + fibNMinus2; 
    fibNMinus2 = fibNMinus1;
    fibNMinus1 = fibN;
  }
  return fibN;
}
console.log(fibIterative(10)); // 55

分治(divide and conquer)。分治的意思就是分而治之。
递归:

function fibRecursive(n){
  // 基本条件
  if (n < 1) return 0; 
  // 基本条件
  if (n <= 2) return 1; 
  // 递归+分治
  return fibRecursive(n - 1) + fibRecursive(n - 2); 
}
console.log(fibRecursive(10)); // 55

递归中的记忆函数记忆函数
经常和递归结合起来使用这里解决的重复计算问题,在算法中也被称为重叠子问题,而记忆函数就是一个备忘录。

function fibMemo(n, memo = [0, 1, 1]) {
    if (memo[n]) {
        return memo[n];
    }
    // 递归+分治+闭包
    memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
    return memo[n];
}
console.log(fibMemo(10)); // 55

递归中的尾递归
尾递归的意思就是在函数的尾部执行递归的调用。通过用尾递归的方式,最多递归 n 次,因为在每次递归的过程中都会 n-1。

function fibTailRecursive(n, lastlast, last){
  if (n == 0) {
    return lastlast;
  }
  if (n == 1) {
    return last;
  }
  return fibTailRecursive(n-1, last, lastlast + last);
}
console.log(fibTailRecursive(10, 0, 1)); // 55

递归中的内存管理
在我们用递归的时候,如果没有很好的控制,就会遇到这个性能问题。所以下面,我们再来看看递归中的内存管理。
JavaScript 从 ES6 版本的标准开始,定义了尾调用优化。里面提到,如果一个函数是一个函数里的最后一个动作,它会被当做跳转而不是子程序来处理。也就是说这个代码会不停地被重复,所以这也是为什么要有一个基本条件的重要性。在实际操作中,绝大多数浏览器都会自己定义一个防止栈溢出的限制,比如 Chrome 在下面的一个无限循环的例子中调用了 13952 次之后,就出现了一个超出最大栈范围的错误消息,并且停止了递归。
递归复杂度计算
主定理
n 是问题规模的大小,a 是子问题个数,n/b 是每个子问题的大小,O(n^{c}) 是将原问题分解和将子问题的解合并的时间。
基于 c 和 log_{b}(a) 的对比,会有三种结果。log_{b}(a) 代表了 aT(n/b),即解决当前层问题所需要的时间复杂度;c 代表了 O(n^{c}),即将原问题分解和将子问题的解合并的时间。
day12 JS语义分析该用迭代还是递归?_第1张图片
不符合主定理公式的形式,可以尝试使用递推公式和递归树。

你可能感兴趣的:(前端javascript)