浅谈JS中的递归

凯特林
• 阅读 1666

浅谈JS中的递归

一、递归

递归(英语:Recursion)

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

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

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

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

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

使用迭代的方式,如下:

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

浅谈JS中的递归

为了计算 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,6) // 120  

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

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

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

factorial(5,6) // 120  

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

二、应用场景

递归的场景有很多,列举几个我们常见的,如

数组求和

function sum(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;  
};  
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
徐小夕 徐小夕
3年前
如何优雅的使用javascript递归画一棵结构树
递归和尾递归简单的说,递归就是函数自己调用自己,它做为一种算法在程序设计语言中广泛应用。其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的
Irene181 Irene181
2年前
一篇文章带你了解Python递归函数
一、什么是递归函数?在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。二、函数的递归调用原理实际上递归函数是在栈内存上递归执行的,每次递归执行一次就会耗费一些栈内存。栈内存的大小是限制递归深度的重要因素三、案例分析1.求阶乘计算阶乘n!1x2x3x…xn,可以用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Stella981 Stella981
2年前
Python之函数递归与迭代
函数递归:  定义:程序调用自身的编程技巧称为递归(recursion)。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量
Wesley13 Wesley13
2年前
JAVA学习入门篇_递归结构
递归结构递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。​利用递归可以用简单的程序来解决一些复杂的问题。比如:斐波那契数列的计算、汉诺塔、快排等问题。​递归结构包括两个部分:​1.定义递归头。解答:什么
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
小万哥 小万哥
1星期前
C++ 递归与面向对象编程基础
C递归递归是一种使函数调用自身的技术。这种技术提供了一种将复杂问题分解为简单问题的方法,从而更容易解决问题。递归可能有点难以理解。理解其工作原理的最佳方法是通过实验来尝试。递归示例将两个数字相加很容易做到,但将一系列数字相加就更复杂了。在下面的示例中,