首页 > 其他分享 >举例说明你对尾递归的理解,有哪些应用场景

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

时间:2024-05-28 18:10:50浏览次数:22  
标签:场景 return 函数 递归 pow result obj 举例说明

一、递归

递归(英语:Recursion)

在数学与计算机科学中,是指在函数的定义中使用函数自身的方法

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数

其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

一般来说,递归需要有边界条件、递归前进阶段和递归返回阶段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回

下面实现一个函数 pow(x, n),它可以计算 x 的 n 次方

使用迭代的方式,如下:

function pow(x, n) {
  let result = 1;

  // 再循环中,用 x 乘以 result n 次
  for (let i = 0; i < n; i++) {
    result *= x;
  }
  return result;
}

使用递归的方式,如下:

function pow(x, n) {
  if (n == 1) {
    return x;
  } else {
    return x * pow(x, n - 1);
  }
}

pow(x, n) 被调用时,执行分为两个分支:

             if n==1  = x
             /
pow(x, n) =
             \
              else     = x * pow(x, n - 1)

也就是说pow 递归地调用自身 直到 n == 1

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

  1. pow(2, 4) = 2 * pow(2, 3)
  2. pow(2, 3) = 2 * pow(2, 2)
  3. pow(2, 2) = 2 * pow(2, 1)
  4. pow(2, 1) = 2

因此,递归将函数调用简化为一个更简单的函数调用,然后再将其简化为一个更简单的函数,以此类推,直到结果

二、尾递归

尾递归,即在函数尾位置调用自身(或是一个尾调用本身的其他函数等等)。尾递归也是递归的一种特殊情形。尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数

尾递归在普通尾调用的基础上,多出了2个特征:

  • 在尾部调用的是函数自身
  • 可通过优化,使得计算仅占用常量栈空间

在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出

这时候,我们就可以使用尾递归,即一个函数中所有递归形式的调用都出现在函数的末尾,对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误

实现一下阶乘,如果用普通的递归,如下:

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

如果n等于5,这个方法要执行5次,才返回最终的计算表达式,这样每次都要保存这个方法,就容易造成栈溢出,复杂度为O(n)

如果我们使用尾递归,则如下:

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

可以看到,每一次返回的就是一个新的函数,不带上一个函数的参数,也就不需要储存上一个函数了。尾递归只需要保存一个调用栈,复杂度 O(1)

二、应用场景

数组求和

function sumArray(arr, total) {
    if(arr.length === 1) {
        return total
    }
    return sum(arr, total + arr.pop())
}

使用尾递归优化求斐波那契数列

function factorial2 (n, start = 1, total = 1) {
    if(n <= 2){
        return total
    }
    return factorial2 (n -1, total, total + start)
}

数组扁平化

let a = [1,2,3, [1,2,3, [1,2,3]]]
// 变成
let a = [1,2,3,1,2,3,1,2,3]
// 具体实现
function flat(arr = [], result = []) {
    arr.forEach(v => {
        if(Array.isArray(v)) {
            result = result.concat(flat(v, []))
        }else {
            result.push(v)
        }
    })
    return result
}

数组对象格式化

let obj = {
    a: '1',
    b: {
        c: '2',
        D: {
            E: '3'
        }
    }
}
// 转化为如下:
let obj = {
    a: '1',
    b: {
        c: '2',
        d: {
            e: '3'
        }
    }
}

// 代码实现
function keysLower(obj) {
    let reg = new RegExp("([A-Z]+)", "g");
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            let temp = obj[key];
            if (reg.test(key.toString())) {
                // 将修改后的属性名重新赋值给temp,并在对象obj内添加一个转换后的属性
                temp = obj[key.replace(reg, function (result) {
                    return result.toLowerCase()
                })] = obj[key];
                // 将之前大写的键属性删除
                delete obj[key];
            }
            // 如果属性是对象或者数组,重新执行函数
            if (typeof temp === 'object' || Object.prototype.toString.call(temp) === '[object Array]') {
                keysLower(temp);
            }
        }
    }
    return obj;
};

参考文献

  • https://zh.wikipedia.org/wiki/%E5%B0%BE%E8%B0%83%E7%94%A8

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

 

标签:场景,return,函数,递归,pow,result,obj,举例说明
From: https://www.cnblogs.com/smileZAZ/p/18218605

相关文章

  • 金融核心系统数据库升级路径与场景实践
    走向现代数据架构,数据库成为金融数字化转型关键环节数字化转型的本质是利用数据,重塑传统业务与组织模式,构建企业的新型竞争力。随着数字经济时代的到来,数据量正在从TB级跃升至PB级、甚至ZB级。根据IDC测算,我国数据总量预计2025年将高达48.6ZB,占全球总量的27.8%。如今数据总量......
  • 企业级OV SSL证书的应用场景和加密手段
    为了保护数据传输的安全性与用户隐私,企业级OVSSL(OrganizationValidationSSL)证书成为众多企业的首选安全解决方案。本文将深入探讨OVSSL证书的应用场景及其实现数据加密的核心手段,为企业构建坚不可摧的在线信任桥梁提供指南。一、企业级OVSSL证书概述OVSSL证书,即组织验证型......
  • 10 函数的应用:函数递归
    目录一、什么是递归(一)概念(二)递归的思想二、递归的条件三、递归的举例(一)分析与代码的实现四、递归与迭代(一)递归的缺陷(二)迭代(三)举例体现递归与迭代的区别五、有意思的点(一)递推的写法(二)拓展学习1、青蛙跳台问题2、汉诺塔问题(儿童益智游戏)一、什么是递归(一)概......
  • FreeRtos进阶——栈保存现场的几种场景
    MCU架构在认识栈的结构前,我们先来认识以下单片机的简单架构。在我们的CPU中有着很重要的一个模块——寄存器(R0-R15),其中R13,R14,R15的别称分别为SP栈顶指针、LR返回地址、PC当前指令地址。外部RAM是单片机的内存(当我们在使用栈时就会在内存中划分一块空间作为栈空间)。Code是......
  • 递归
    <!--修改二级字段-->constlist=this.pcTreeData.map(item=>{constarr=item.btnInfoVos.map(i=>{return{...i,id:i.btnCode,componentTitle:i.btnName}......
  • 动态规划--图论中使实用场景概述
    目录一 动态规划概述二 动态规划在图论中应用场景三c实例1.**最短路径问题(Dijkstra算法)**:2.**最小生成树问题(Kruskal算法)**:一 动态规划概述动态规划(DynamicProgramming,简称DP)是一种用于解决具有重叠子问题和最优子结构特性的问题的优化方法。动态规划通过将原......
  • MySQL - [05] 需求&场景
      一、生成测试数据(1)首先,有表如下createtableapp_user(`id`bigint(20)notnullauto_incrementcomment'用户id',namevarchar(50)notnullcomment'用户名',emailvarchar(50)comment'邮箱',phonevarchar(20)comment'......
  • 智慧工厂新篇章:可视化三维场景引领未来制造
    在科技日新月异的今天,我们似乎总是在不断追求着更加高效、智能的生产方式。 传统的工厂管理方式往往依赖于平面图纸、纸质文档和现场巡查,这不仅效率低下,而且容易出错。而三维可视化技术通过3D建模和虚拟现实技术,将工厂内部的各个角落、设备、管线等细节都一一呈现在眼前,让管理......
  • Web Service和Web API理解和使用场景
    WebService理解:WebService是一种基于网络的服务,它使用标准化的消息传递协议,最典型的是基于SOAP(SimpleObjectAccessProtocol)协议。SOAP使用XML格式封装数据,定义了消息的结构和传输方式,因此它是一个重量级的解决方案。WebService支持跨平台、跨语言的通信,常用于企业内......
  • java 实现递归方法
    递归是一种通过调用自身的函数来解决问题的方法。在Java中,编写递归可以按照以下步骤进行:确定基本情况:首先确定递归函数的基本情况,即递归终止条件。通常,这是一个简单的情况,无需进一步递归调用即可解决。定义递归方法:编写一个方法来解决问题,并在方法中判断是否需要进行递归调......