尾递归及蹦床函数

2022-04-11 11:07:37
什么是尾递归,JavaScript 实现通用版尾递归转迭代函数,为什么 JavaScript 运行环境都不再支持自动尾递归优化。

求和函数的递归和尾递归的函数调用栈上下文

尾递归

求和

求 1 到 n 的和,函数调用栈的情况如上图所示

递归版

function sum (n) {
  if (n === 1) return 1
  return n + sum(n - 1)
}

尾递归版


function sumWithTailRecursion (n, prevSum = 0) {
  if (n === 1) return prevSum + 1
  return sumWithTailRecursion(n - 1, prevSum + n)
}

迭代版

尾递归函数可以被改写成迭代形式

function sumWithIteration(n) {
  let prevSum = 0
  while (n >= 1) {
    prevSum += n
    n--
  }
  return prevSum
}

蹦床函数

通用尾递归转换迭代函数

const trampoline = f => (...args) => {
  const result = f(...args)
  while (typeof result === 'function') result = result()
  return result
}

用蹦床函数改写递归

const sumWithIteration = trampoline(sumWiteRecursion)

尾递归支持的情况

动态规划、记忆化递归:求解《131. 分割回文串》《剑指 Offer II 086. 分割回文子字符串》《132. 分割回文串 II》《剑指 Offer II 094. 最少回文分割》《1278. 分割回文串 III》
用动态规划、记忆化递归求解《131. 分割回文串》《剑指 Offer II 086. 分割回文子字符串》《132. 分割回文串 II》《剑指 Offer II 094. 最少回文分割》和《1278. 分割回文串 III》
动态规划,中心扩散:求解《647. 回文子串》和《5. 最长回文子串》
动态规划,递归和迭代两种方式中心扩散,求解《647. 回文子串》和《5. 最长回文子串》
双指针判断回文字符串:求解《125. 验证回文串》《018. 有效的回文》《680. 验证回文字符串 Ⅱ》和《019. 最多删除一个字符得到回文》
使用双指针判断回文字符串,求解《125. 验证回文串》《剑指 Offer II 018. 有效的回文》《680. 验证回文字符串 Ⅱ》和《剑指 Offer II 019. 最多删除一个字符得到回文》