翼度科技»论坛 编程开发 JavaScript 查看内容

举例说明你对尾递归的理解,有哪些应用场景

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15

一、递归

递归(英语:Recursion)
在数学与计算机科学中,是指在函数的定义中使用函数自身的方法
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数
其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
一般来说,递归需要有边界条件、递归前进阶段和递归返回阶段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回
下面实现一个函数 pow(x, n),它可以计算 x 的 n 次方
使用迭代的方式,如下:
  1. function pow(x, n) {
  2.   let result = 1;
  3.   // 再循环中,用 x 乘以 result n 次
  4.   for (let i = 0; i < n; i++) {
  5.     result *= x;
  6.   }
  7.   return result;
  8. }
复制代码
使用递归的方式,如下:
  1. function pow(x, n) {
  2.   if (n == 1) {
  3.     return x;
  4.   } else {
  5.     return x * pow(x, n - 1);
  6.   }
  7. }
复制代码
pow(x, n) 被调用时,执行分为两个分支:
  1.              if n==1  = x
  2.              /
  3. pow(x, n) =
  4.              \
  5.               else     = x * pow(x, n - 1)
复制代码
也就是说pow 递归地调用自身 直到 n == 1

为了计算 pow(2, 4),递归变体经过了下面几个步骤:

  • pow(2, 4) = 2 * pow(2, 3)
  • pow(2, 3) = 2 * pow(2, 2)
  • pow(2, 2) = 2 * pow(2, 1)
  • pow(2, 1) = 2
因此,递归将函数调用简化为一个更简单的函数调用,然后再将其简化为一个更简单的函数,以此类推,直到结果
二、尾递归

尾递归,即在函数尾位置调用自身(或是一个尾调用本身的其他函数等等)。尾递归也是递归的一种特殊情形。尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数
尾递归在普通尾调用的基础上,多出了2个特征:

  • 在尾部调用的是函数自身
  • 可通过优化,使得计算仅占用常量栈空间
在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出
这时候,我们就可以使用尾递归,即一个函数中所有递归形式的调用都出现在函数的末尾,对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误
实现一下阶乘,如果用普通的递归,如下:
  1. function factorial(n) {
  2.   if (n === 1) return 1;
  3.   return n * factorial(n - 1);
  4. }
  5. factorial(5) // 120
复制代码
如果n等于5,这个方法要执行5次,才返回最终的计算表达式,这样每次都要保存这个方法,就容易造成栈溢出,复杂度为O(n)
如果我们使用尾递归,则如下:
  1. function factorial(n, total) {
  2.   if (n === 1) return total;
  3.   return factorial(n - 1, n * total);
  4. }
  5. factorial(5, 1) // 120
复制代码
可以看到,每一次返回的就是一个新的函数,不带上一个函数的参数,也就不需要储存上一个函数了。尾递归只需要保存一个调用栈,复杂度 O(1)
二、应用场景

数组求和
  1. function sumArray(arr, total) {
  2.     if(arr.length === 1) {
  3.         return total
  4.     }
  5.     return sum(arr, total + arr.pop())
  6. }
复制代码
使用尾递归优化求斐波那契数列
  1. function factorial2 (n, start = 1, total = 1) {
  2.     if(n <= 2){
  3.         return total
  4.     }
  5.     return factorial2 (n -1, total, total + start)
  6. }
复制代码
数组对象格式化
  1. let a = [1,2,3, [1,2,3, [1,2,3]]]
  2. // 变成
  3. let a = [1,2,3,1,2,3,1,2,3]
  4. // 具体实现
  5. function flat(arr = [], result = []) {
  6.     arr.forEach(v => {
  7.         if(Array.isArray(v)) {
  8.             result = result.concat(flat(v, []))
  9.         }else {
  10.             result.push(v)
  11.         }
  12.     })
  13.     return result
  14. }
复制代码
参考文献


  • https://zh.wikipedia.org/wiki/%E5%B0%BE%E8%B0%83%E7%94%A8
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

 

来源:https://www.cnblogs.com/smileZAZ/p/18218605
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具