【问题标题】:Recursively add digits递归添加数字
【发布时间】:2017-01-13 17:33:28
【问题描述】:

我在 LeetCode 上遇到了一个问题,需要复习一下递归。这是问题:

Given a non-negative integer num, repeatedly add all its digits until 
the result has only one digit.

For example:

Given num = 38, the process is like: 3 + 8 = 11, 1 + 1 = 2. Since 2 has only one digit, 
return it.

我已经看到了使用 while 循环的答案,但我想尝试使用递归。 这是我目前所拥有的:

public int AddDigits(int num) {
    if(num > 9)
    {
        num = (num%10) + (num/10);
        AddDigits(num);
    }
    else{
        return num;
    }
}

首先,我得到"Not all code paths return a value.",但基于if 布尔检查,这不应该吗?即使我在 else 块之后添加return num,我仍然得到 11。使用 29 的输入,我的解决方案返回 11,即使 num最终变为 2。使用上面的递归解决方案,return num 部分出现几次(我用 Console.WriteLine 语句进行了测试) - 这是由于堆栈吗?

这是我想知道的 - 为什么代码会在递归调用之后出现 - 或者递归调用通常不应该包含递归调用之后的代码?

另外,如何在不使用 while 循环的情况下使用递归?

【问题讨论】:

  • return AddDigits(num);
  • “为什么在递归调用之后会出现代码 - 或者递归调用通常不应该包含递归调用之后的代码?”在此处查找术语“尾递归”以找到答案。

标签: c# recursion


【解决方案1】:

您需要在两个路径(if 和 else)中都返回值。

可能是这样(将AddDigits(num); 更改为return AddDigits(num); 也有效(在其他答案中给出),顺便说一句,我不知道为什么,但它有效)

public int AddDigits(int num, bool root = true) {
    if(num > 9)
    {
        var sum = num;

        if(root)
        {
            while(sum > 9)
            {
                sum = AddDigits(sum/10, false) + sum%10;
            }
            return sum;
        }
        else return AddDigits(num/10, false) + num%10;
    }
    else{
        return num;
    }
}

这样调用函数

var result = AddDigits(123456789); // ;)

这就是算法的工作原理。

       AddDigits(123456789)   (root of call)         45   (45 is bigger than 9.    AddDigits(45)     9 is not bigger than 9. return result.
                    ||                               /\   repeat. while(sum > 9))       ||           /\
                    \/                               ||                                 \/           ||
   9 + AddDigits(12345678)                           ||                            4 + AddDigits(5)= 9     
                    ||                               ||
                    \/                      9+8+7+6+5+4+3+2+1 = 45
9 + 8 + AddDigits(1234567)                           ||
                    ||                               ||
                   ....                              ||        
                    ||                               ||
                    \/                               ||
            9+8+7+6+5+4+3+2+1 (now going back and summing them all)

【讨论】:

    【解决方案2】:

    这里有一个提示,不会产生正确的值,但它会让你到达那里。

    (另一个提示,你的函数需要两个参数)

    public int AddDigits(int num) {
        if(num > 9)
        {
            num = (num%10) + (num/10);
            return AddDigits(num);
        }
        else{
            return num;
        }
    }
    

    编辑: Python 中的解决方案

    def ra(n):
      if n > 0:
        return n % 10 + ra(n / 10)
      else:
        return 0
    

    【讨论】:

    • “你的函数需要两个参数”?我不明白为什么。
    • 当然,这仅适用于 2 位数字,但即使是 3+,您也可以使用 1 个参数来实现。
    • @NickA 很有趣,没想到使用堆栈来保存残差值。我已经用这样的算法更新了我的答案。
    • 这里有求和数学吗?我很惊讶地看到这确实有效。嗯去检查
    • 不会产生正确的值。不!它确实产生了正确的值。如果您相信我的算法有效,您可以对照您的算法进行检查。从0 循环到int.Max,如果我的算法结果与你的不同,则抛出异常。你永远不会例外!
    【解决方案3】:

    这是一个使用 Linq 的示例:

    using System.Linq;
    
    static int Singularize(int val)
    {
      string str=val.ToString();
      int rslt=Enumerable.Range(0,str.Length).Select(i => str.Substring(i,1)).Select(int.Parse).Sum();
      return (rslt.ToString().Length==1) ? rslt : Singularize(rslt);
    }
    

    返回递归调用本身(必要时)允许 CLR 离开当前调用并移动到递归调用并重用相同的堆栈项。如果您保留当前调用(通过使递归调用返回当前调用的结果),递归链中的每个调用都会将另一根稻草压在骆驼的(堆栈)上,直到骆驼断裂 - 即堆栈溢出错误。

    【讨论】:

      【解决方案4】:

      实际上是两种不同的操作,都可以递归地表述。

      操作1是将一个数字的所有数字相加:

      int AddAllDigits(int n)
      {
          return n < 10 ? n : (n % 10 + AddAllDigits(n / 10));
      }
      

      操作 2 是继续执行操作 1,直到我们只有一个数字:

      int ReduceToSingleDigit(int n)
      {
          return n < 10 ? n : ReduceToSingleDigit(AddAllDigits(n));
      }
      

      所以现在我们可以

      ReduceToSingleDigit(123456789); //gives 9
      

      现在,如果我们可以利用 C#6 的优点,我们可以通过将递归函数简化为表达式体而不是语句体,从而使我们的递归函数看起来更时尚(或令人困惑,具体取决于您的位置)。

      int ReduceToSingleDigit(int n) => n < 10 ? n : ReduceToSingleDigit(AddDigits(n));
      
      int AddDigits(int n) => n < 10 ? n : (n % 10 + AddDigits(n / 10));
      

      可爱。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-20
        • 1970-01-01
        • 1970-01-01
        • 2020-05-16
        • 2013-11-21
        • 2011-11-03
        相关资源
        最近更新 更多