递归函数中断if语句javascript

递归函数中断if语句javascript,javascript,if-statement,recursion,Javascript,If Statement,Recursion,我正在尝试编写一个函数,将一个数量转换为指定数量的硬币。为此,我在自己调用一个函数(我认为这是递归编程?)。我有两个问题 当我在if语句的else部分调用函数本身时,我得到了错误消息。“超出了最大调用堆栈大小”?不确定这是为什么,因为上面的调用是相同的,并且在注释掉第二个调用时工作良好 另外,当第二个调用被注释掉并且函数运行时,变量total应该随着每次调用增加1。但是它并没有超过1。我想这可能是因为变量在每次调用时都被重置为0。但是,余数变量也设置为0,每次都会减小其值 有人能解释一下这里发生

我正在尝试编写一个函数,将一个数量转换为指定数量的硬币。为此,我在自己调用一个函数(我认为这是递归编程?)。我有两个问题

  • 当我在
    if
    语句的
    else
    部分调用函数本身时,我得到了错误消息。“超出了最大调用堆栈大小”?不确定这是为什么,因为上面的调用是相同的,并且在注释掉第二个调用时工作良好

  • 另外,当第二个调用被注释掉并且函数运行时,变量
    total
    应该随着每次调用增加1。但是它并没有超过1。我想这可能是因为变量在每次调用时都被重置为0。但是,
    余数
    变量也设置为0,每次都会减小其值

  • 有人能解释一下这里发生了什么吗?如何最好地解决这个问题

    谢谢

    function amountCoins(amt, coins) {
    
    var remainder = 0;
    total = 0;
    
    if(amt >= coins[0]) {
        total += 1;
        remainder = (amt - coins[0]); 
        return amountCoins(remainder, coins);
    } else {
        coins.shift();
        //return amountCoins(remainder,coins);
    
    }
    alert(total);
    }
    
    amountCoins(121,[20,15,6,1]);
    

    如果我是你,我会使用常规的
    for
    循环:

    var amt = 121;
    var coins = [20, 15, 6, 1];
    
    var coinsUsed = {};
    var totalCoins = 0;
    
    for (var i = 0; i < coins.length; i++) {
        if (coins[i] <= amt) {
            coinsUsed[coins[i]] = Math.floor(amt / coins[i]);
            amt -= coinsUsed[coins[i]] * coins[i];
            totalCoins += coinsUsed[coins[i]];
        } else {
            coinsUsed[coins[i]] = 0;
        }
    }
    
    console.log(coinsUsed, totalCoins);
    

    问题是你的算法永远不会结束。每个递归函数都必须有一个端点,否则将产生堆栈溢出,因为递归调用存储在堆栈中

    这将是一个解决方案:

    function amountCoins(amt, coins, total) {    
        var remainder = 0;
        total = total || 0;   //Default value 0 (for the first call)    
        if(coins.length == 0) return total;   //This is the ending condition (no coins)
        if(amt >= coins[0]) {
           total += 1;
           remainder = (amt - coins[0]); 
           return amountCoins(remainder, coins, total);
        } else {
           coins.shift();
           return amountCoins(remainder, coins, total);
        }   
    }
    var coins = amountCoins(121,[20,15,6,1]);  //Returns: 6
    alert(coins);
    
    如您所见,我将
    total
    转换为一个参数,这样我们就可以在调用之间存储它


    希望这有帮助。干杯

    我想这就是你想要做的:

    function amountCoins(amt, coins) {
      var remainder = 0;        
      if(coins.length == 0) {
        return 0;
      } else if(amt >= coins[0]) {
          remainder = (amt - coins[0]); 
          return amountCoins(remainder, coins)+1;
      } else {
          coins.shift();
          return amountCoins(remainder,coins);    
      }
    }
    
    var total = amountCoins(121,[20,15,6,1]);
    alert(total)
    
    这里有一些东西

  • 递归算法需要一个终止条件。当函数递归调用自身时,它应该在某个点停止链。如果没有,程序将耗尽内存来容纳链中的所有调用。因为这是一种潜在的危险情况,像javascript这样的编程语言限制了递归调用的深度。这就是超出最大调用堆栈大小的
    错误的含义

    在您的程序中,逻辑上,终止条件是当硬币用完时。那么
    coins.length==0
    检查是否返回一个
    0
    总数(这反过来会使向上的链种子化)将解决问题

  • 通常在递归算法中,结果是向后传递的,沿着调用链向上传递,而不是存储在外部变量中。因此,递增的总值用返回语句表示,如
    returnamountcoins(余数,coins)+1


  • 最后,这个问题可以很容易地用for循环实现。尝试通过解开递归调用链来进行思考,您将找到一个循环解决方案。

    对原始代码的更正将有效:

    function amountCoins(amt, coins, total) {
    
        total = total || 0; 
        if (amt === 0) return total;
    
        // throw out all the coins that are too big
        while (amt < coins[0]) coins.shift(); 
    
        // use the largest coin possible, then recurse
        total += 1;
        remainder = amt - coins[0]; 
        return amountCoins(remainder, coins, total);
    }
    
    alert(amountCoins(121,[20,15,6,1])); // alerts 7
    
    函数amountCoins(amt,coins,total){
    总计=总计| | 0;
    如果(金额===0)返回总计;
    //扔掉所有太大的硬币
    而(amt

    如果。。。else
    语句,逻辑更清晰一些。imo

    您必须返回总计,不仅是在没有剩余硬币的情况下,而且在最后一枚硬币的价值大于剩余价值的情况下:

    function amountCoins(amt, coins, total) {
    
        total = total || 0;
    
        if (coins.length == 0 || (amt < coins[0] && coins.length == 1)) return total;
    
        if (amt >= coins[0]) {
            total += 1;
            remainder = (amt - coins[0]); 
            return amountCoins(amt - coins[0], coins, total);
        } else {
            coins.shift();
            return amountCoins(remainder, coins, total);
        }
    }
    
    函数amountCoins(amt,coins,total){
    总计=总计| | 0;
    如果(coins.length==0 | |(amt=硬币[0]){
    总数+=1;
    余数=(金额-硬币[0]);
    返回金额硬币(金额-硬币[0],硬币,总计);
    }否则{
    硬币。移位();
    返回金额硬币(剩余、硬币、总数);
    }
    }
    
    您可以使用
    .reduce()
    作为替代方法

    我们并不需要简单数学能够处理的循环

    var total = [20,15,6,1].reduce(function(obj, denomination) {
        return {
            total: obj.total + Math.floor(obj.amount / denomination),
            amount: obj.amount % denomination
        };
    }, {amount:121, total:0}).total;
    

    或者迭代数组

    var arr = [20,15,6,1], amount = 121, total = 0;
    
    for (var i = 0; i < arr.length; ++i) {
        total += Math.floor(amount / arr[i]);
        amount %= arr[i];
    }
    
    var arr=[20,15,6,1],金额=121,总计=0;
    对于(变量i=0;i
    您必须有一个非递归返回,调用链可以在这里结束。如果没有,函数将无限地调用自身并填充堆栈。这就是为什么“超过了最大调用堆栈大小”。当coins数组为空时,一个好的退出点可能是,在这种情况下,您应该返回total。函数应该返回指定数量所需的总硬币的最小数量。我认为在递归中保持变量值就是在参数中使用它是对的吗?reduce的出色使用
    var arr = [20,15,6,1], amount = 121, total = 0;
    
    for (var i = 0; i < arr.length; ++i) {
        total += Math.floor(amount / arr[i]);
        amount %= arr[i];
    }