【JS】JavaScript中常遇到的递归问题

227 阅读6分钟

序言

递归是一种编程技巧,它通过在函数内部调用自身来解决问题。

在 JavaScript 中,递归常用于处理可以被分解为相同或相似子问题的情况。

递归在解决一些问题时非常有用,特别是那些可以被分解为相同或相似子问题的情况。但要注意,在使用递归时,确保存在基本情况有结束条件以避免无限递归,并且每次递归调用都能使问题规模减小,否则可能导致栈溢出等问题。

递归计算阶乘

让我们以一个简单的例子来说明递归的使用:计算一个正整数的阶乘。

function mul(n){ //mul 就是可以计算阶乘
    //n! = n * (n - 1)!
    if(n === 0){
        return 1
    }
    return n * mul(n - 1)
}
console.log(mul(5)); //输出120

如果我们要想计算一个数的阶乘,如果数字很小的话我们也许可以硬着头皮算出来,但是当这个数足够大的时候我们不得不求助计算机,在上述代码中,mul 函数接受一个参数 n,表示要计算阶乘的数。首先,我们定义了基本情况,即当 n 等于 0 时,阶乘的结果为 1,这是一个无需递归的特殊情况。然后,在递归调用中,我们将 n 乘以 mul(n - 1),即将问题分解为较小的子问题,直到 n 递减到 0(基本情况)。

斐波那契数列的递归算法

// 斐波那契数列
// 1 1 2 3 5 8 13 21 34 55.....
function fb(n){
    // fb(n) = fb(n-1) + fb(n-2)

    if(n == 1 || n == 2 ){
        return 1
    }
    return fb(n-1) + fb(n-2) 
}

console.log(fb(10));// 输出55

首先我们在用递归的前提是这个数列一定是可以找到规律并且有终止条件的,对于斐波那契数列这种有规律的序列,使用递归算法可以更高效地计算出结果。特殊情况我们分为当 n 等于 1 或 2 时,直接返回 1,否则,将问题分解为计算第 n-1 项和第 n-2 项的和,并通过递归调用 fb 函数来求解这两项的值。然后我们打印出斐波那契数列第十位的值为55

图片.png

利用递归算法将嵌套数组扁平化

var arr = [1,[2,[3,4]]]

var newArr = arr.flat(Infinity)
console.log(newArr);

运行这段代码的结果将是 [1, 2, 3, 4]

在ES6版本JS更新了falt()方法,flat() 方法会按照指定的深度将数组展开,如果没有提供深度参数,则默认为 1。在这个例子中,我们使用了 Infinity (无穷大)作为深度参数,表示要展开所有嵌套层级。

原始数组 arr 是一个多层嵌套的数组,包含了数字和子数组。flat(Infinity) 方法将对整个数组进行展开,并得到一个新的一维数组 newArr。展开的结果是将原始数组中所有的元素提取出来,不论嵌套多少层。最后使用console.log() 方法打印出新的数组 newArr,结果是 [1, 2, 3, 4]

接下来我们来看一道面试题,面试官:将这个数组展开,不能使用JS提供的方法,你有什么方法实现像上面一样的效果吗?

//面试题

var arr = [1,[2,[3,4]]]

//递归
function flatten(arr){
    var result = [];
    for(var i = 0; i < arr.length; i++){
        if(Array.isArray(arr[i])){
        
            var nextArr = flatten(arr[i]);
            result = result.concat(nextArr)

        }else{
            result.push(arr[i])
        }
    }
    return result
}
console.log(flatten(arr));

要实现同样效果我们也可以选择用递归来展开嵌套数组,并将展开后的元素放入到一个新的数组中。

思路就是函数 flatten(arr) 是一个递归函数,它接受一个数组作为参数。函数内部使用一个 result 数组来保存展开后的元素。然后,使用 for 循环遍历传入的数组 arr 的每一个元素。

对于数组中的每个元素,首先检查它是否为一个数组,我们可以使用 Array.isArray() 方法进行判断。如果是数组,则继续递归调用 flatten() 函数,并将返回的展开后的数组赋值给 nextArr

然后,使用 result.concat(nextArr)nextArr 中的元素连接到 result 数组中。通过递归的方式,可以展开多层嵌套的数组。

如果元素不是数组,则说明它是一个基本类型的元素,直接将其添加到 result 数组中,使用 result.push(arr[i])

最后,递归函数返回 result 数组,包含了展开后的所有元素。 使用 console.log(flatten(arr)) 打印出调用 flatten() 函数得到的结果,结果为 [1, 2, 3, 4]

reduce方法计算所有元素值的和

var arr = [1, 2, 3, 4, 5, 6, 7]

var sum = arr.reduce(
    function(pre,item,index,arr){
        return pre + item
},0)

console.log(sum);

数组的 reduce() 方法可以用来进行累加求和操作。数组的 reduce() 方法来进行累加求和操作。

reduce() 方法接受两个参数:一个回调函数和一个初始值。

回调函数在每次迭代中被调用,它接受四个参数:累加器 pre、当前元素 item、当前索引 index 和原始数组 arr

在每次迭代中,回调函数将累加器 pre 与当前元素 item 相加,并返回新的累加结果给pre。

初始值参数为 0,表示初始累加器的值为 0

reduce() 方法会遍历数组中的每个元素,并将每次回调函数的返回值作为下一次迭代的累加器的值。

最后,通过 console.log() 方法将累加的结果 sum 打印出来,结果为 28。这是因为数组 [1, 2, 3, 4, 5, 6, 7] 中的元素相加得到的总和为 28

reduce方法结合递归扁平化嵌套数组

既然reduce可以实现回调,那么我们也可以用reduce方法将嵌套数组遍历展开,然后利用回调将遍历的结果返回到一个新数组,最终利用concat方法连接每次回调生成的数组实现嵌套数组扁平化。

var arr = [1,[2,[3,4]]]

function flaten(arr){
    return arr.reduce(function(pre,item){
        return pre.concat(Array.isArray(item) ? flaten(item) : [item])
    },[])
}

console.log(flaten(arr)); 

思路:函数 flaten(arr) 接受一个数组 arr 作为参数,并使用 reduce() 方法遍历该数组的每个元素。对于每个元素,回调函数会分别执行以下操作:

  • 如果该元素是一个数组,则递归调用 flaten() 方法,并将返回的展开后的一维数组与之前的结果数组 pre 连接起来,形成新的结果数组。
  • 如果该元素不是一个数组,则直接将其作为一个单独的元素添加到结果数组 pre 中。

在这个过程中,通过递归的方式,可以展开多层嵌套的数组,并将其中的所有元素合并到一个新的一维数组中。

初始值参数为一个空数组 [],表示初始结果数组为空。

最终,flaten() 函数返回一个新的一维数组,其中包含原始数组中所有的元素。使用 console.log(flaten(arr)) 打印出调用 flaten() 方法得到的结果,结果为 [1, 2, 3, 4]

感谢大家的阅读,点点赞吧♥

如果想了解更多有用的干货,收藏+关注 大厂不迷路

本人的开源Git仓库: gitee.com/cheng-bingw…

更多内容:[前端面试]🔥🔥🔥JavaScript中的原型与原型链 深入探讨访问变量属性时的隐式过程🔥🔥🔥