为什么此尾部递归循环会导致javascript / node中的堆栈溢出?

时间:2019-01-02 01:02:11

标签: javascript node.js

exports.tailLoop = function(A) {
    const asc = A.sort((a,b) => a - b)
    function tailRecur (rest) {
        if (!rest.length) return 0
        const pair = rest.splice(0,2)
        if (pair[0] != pair[1]){
            return pair[0]
        } else {
            return tailRecur(rest)
        }   
    }   
    return tailRecur(asc)
}

我也尝试过使用:

first = rest.shift()
next = rest.shift()

代替拼接方法。

当我输入具有一百万个元素的数组时,它将失败。 我不是正确理解尾递归还是尾递归在100万个大小的数组上不起作用(注意排序在100万个大小的数组上工作正常)

3 个答案:

答案 0 :(得分:2)

要回答评论问题:如何处理节点中的大输入 —您可以always find ways将递归函数转换为非递归函数。有时它并不那么优雅,但是在这种情况下,您基本上是将递归用于循环,这将作为一个简单的循环更快,更容易理解。

类似的东西:

function nonRec(A){
    const asc = A.sort((a,b) => a - b)
    while (asc.length){
        const pair = asc.splice(0,2)
        if (pair[0] != pair[1])
            return pair[0]
    }
    return 0
}

a = [1, 2, 3, 2, 4, 2, 2, 1, 3]
console.log(nonRec(a))

答案 1 :(得分:2)

@Mark已经answered问题了,所以这仅仅是OP代码的重构。

您的代码基本上只是通过一次循环两个项目来检查两个相等的连续项目,这可以通过使用for循环彻底消除优化,以消除对{{ 1}}:

splice

答案 2 :(得分:1)

您可以尝试increasing NodeJS maximum call stack,不确定在这种情况下是否有帮助。

跳过最大调用堆栈的另一种方法是将代码从同步更改为异步

tailLoop = function(A) {
  let resolver;
  const promise = new Promise((res,_rej)=>{
    resolver = res
  })
  const asc = A.sort((a,b) => a - b)
  function tailRecur (rest) {
    if (!rest.length) return 0
    const pair = rest.splice(0,2)
    if (pair[0] != pair[1]){
      resolver(pair[0])
    } else {
      setImmediate(()=>{
        tailRecur(rest)
      })
    }   
  }
  tailRecur(asc)
  return promise
}

现在不会超过最大调用堆栈。

const a = []
for(let i=0;i<10000;i++){
  for(let j=0;j<100;j++){
    a.push(0)
  }
}
a.push(1)

tailLoop(a).then(result=>{
  console.log(result) //1
})

顺便说一下,上面的代码花了几分钟才能得到结果...

我认为您可以找到一种更好的方法/算法来解决此问题。