【问题标题】:Recursive function to count the amount of digits in a number计算数字中位数的递归函数
【发布时间】:2018-06-29 15:55:50
【问题描述】:

有没有办法编写一个递归函数来打印数字中的位数:

-它是一个空函数

-“if”条件为 if(num==0), return

-“else”将调用递归。

我看到了 2 种不同类型的代码,其中一种是“if”条件具有递归调用,而另一种是“return”。但这不是我想要的。

我不擅长递归并试图通过自己编码来理解它,但没有成功。

这是我的代码(我明白为什么它打印 122 而不是 3,但我真的不知道它的编码方式有何不同。帮助任何人吗?)

    #include <iostream>
    #include <string.h>
    using namespace std;
    void numOfDigits(unsigned int num);

    int main(){
        int num = 994;
        numOfDigits(num);
    }
    void numOfDigits(unsigned int num)
    {
        int size = 1;
        if (num==0)
            return;
        else
        {
            if (num / 10 != 0)
                size++;
            numOfDigits(num / 10);
        }
        cout << size;
    }

【问题讨论】:

  • 添加c++标签...
  • @Cocoboom 为什么你需要 main 无效,有什么特别的原因吗?
  • @cauchy -- void main 完全错误。但这并不影响问题本身。 void 要求适用于 numOfDigits
  • @cauchy 不,我的错。但正如皮特贝克尔所说

标签: c++ recursion


【解决方案1】:

使此代码正常工作的快速技巧是将size 设为静态,即更改

int size = 1;

static int size = 1;

但这仅在您第一次调用该函数时有效。

要获得更强大的解决方案,到目前为止,在每次调用函数时,您都必须传递计数:

void numOfDigits(unsigned int num, int countSoFar = 0) {
    if (num == 0)
        std::cout << countSoFar << '\n';
    else
        numOfDigits(num / 10, countSoFar + 1);
}

【讨论】:

  • 谢谢。你说的第一次是什么意思?我只调用了一次递归,静态 int size=1;但它会打印 333。当您说 1 次时,您的意思是一次交互?
  • 如果countSoFar不是参考,从长远来看它是如何打印的?也就是说,如果我们想打印一次,或者以后甚至可以用它做其他事情?
  • @Aziuth - 它的更新值被传递给下一个递归调用。试试看。
  • @Cocoboom -- 试试看。
  • 再一次,如果我们以后想用它做点什么怎么办?调用它,检索数字,然后将向量调整为该大小?此外,我们在这里混合职责,计算位数并打印出来。
【解决方案2】:

您在 numOfDigits() 函数中有许多错误。

  1. 首先,每次调用函数时,都要声明一个名为 size 的新局部变量。这与调用函数中定义的“大小”无关。要看到这一点,请在初始化后打印尺寸。要解决此问题,请将大小设为静态;然后,每次调用该函数时,它都会使用相同的静态变量。
  2. 当您在函数末尾打印 size 时,它​​只是在函数运行后给出 size 变量的值。即使您将 size 设置为静态,您也会打印 size 的中间值。解决此问题的一种简单方法是允许函数返回大小,您只需在主函数中打印函数的值。

    #include <iostream>
    #include <string.h>
    using namespace std;
    int numOfDigits(unsigned int num);
    
    int main(){
            int num = 994;
            cout<<numOfDigits(num);
    }
    int numOfDigits(unsigned int num)
    {
            static int size = 1;
            if (num==0)
                return 0;
            else
            {
                if (num / 10 != 0)
                    size++;
                numOfDigits(num / 10);
            }
            return size ;
    }
    

确保根据需要使用 (num == 0) 放置案例;在这种情况下,它会打印 0 作为答案。

PS:打印数字后一定要留一个空格。否则你可能会认为 1 2 2(这是实际打印的数字)是数字 122。

【讨论】:

    【解决方案3】:

    您可以通过引用传递一个值并使用它,每次调用该函数时初始化ans=0

    void recursive(unsigned int num,int &ans){
        if(num == 0){
            return;
        }
        else{
            ans++;
            recursive(num/10,ans);
        }
    }
    

    thisthis

    【讨论】:

    • 不要忘记将ans设置为0每次调用此函数时。这不是一个可靠的解决方案。
    • @PeteBecker,我提到过
    • 不,您没有提到每次都必须重置该值。您的回答说要将其初始化为 0。
    • @PeteBecker 知道了
    【解决方案4】:

    不使用任何全局变量,这段代码就可以工作。请注意:您将参数声明为 unsigned int,但您为函数提供的数字是有符号整数。

     #include <iostream>
        #include <string.h>
        using namespace std;
        void numOfDigits(unsigned int num, unsigned int& digits);
    
        int main(){
            unsigned int num = 93934;
            unsigned int digits = 1;
            numOfDigits(num, digits);
            cout <<digits <<endl;
    
            return 0;
        }
    
        void numOfDigits(unsigned int num, unsigned int& digits) {
            if (num==0){
                return;
            }else{
                if (num / 10 != 0){
                    ++digits;
                    numOfDigits(num / 10, digits);
                }
            }
        }
    

    【讨论】:

      【解决方案5】:

      尝试全局声明大小,因为每次函数执行时都会初始化它

         int size = 1;
      void numOfDigits(unsigned int num)
          {
      
              if (num==0)
                  return;
              else
              {
                  if (num / 10 != 0)
                      size++;
                  numOfDigits(num / 10);
              }
          }
      

      打印main里面的size值

      【讨论】:

      • 它在每次递归调用时输出,并且不能被调用两次(很容易)...
      • 不好。每次调用该函数时,必须将 size 设置为 1。这很脆弱,有更好的方法来做到这一点。
      • @yassin -- 可以调用两次;您必须在每次调用之前将 size 设置为 1。
      • 在这个例子中效果很好。一旦你有一个不是用于培训目的的程序,事情就会变糟。不要习惯全局变量。
      【解决方案6】:
      #include <iostream>
      #include <string.h>
      using namespace std;
      void numOfDigits(unsigned int num);
      
      void main(){
          int num = 994;
          int size = 1;
          cout << numOfDigits(num, size);
      }
      void numOfDigits(unsigned int num, int &size)
      {
      
          if (num==0)
              cout<<size;
          else
          {
              if (num / 10 != 0)
                  size++;
              numOfDigits(num / 10, size);
          }
      
      }
      

      【讨论】:

      • 您不能从返回 void 的函数中使用 return size。您可以将numOfDigits 的返回类型更改为int,但这违反了问题中的要求之一。即使有这样的改变,这个函数也不会给出正确的答案。
      • 我已经进行了编辑。我没有先看到 void main。
      【解决方案7】:

      我认为您的误解是您认为size一个 单变量。但是,在您的函数的每次调用中,您都有一个不同的调用。此外,您的函数的每次调用都会再次打印它。

      main 中的 cout 什么都不做,因为它会打印一个没有内容的 void。

      通常,您会给它一个返回参数,即累积的数字,例如

      int numOfDigits(unsigned int num);
      void main(){
          cout << numOfDigits(994) << endl;
      }
      

      如果您出于某种原因不想这样做,您可以通过参考来做到这一点:

      int numOfDigits(unsigned int num, unsigned int& digits);
      void main(){
          unsigned int digits;
          numOfDigits(994,digits);
          cout << digits << endl;
      }
      

      但这不是那么好。

      也就是说,就风格而言,我要做的就是让它成为一个循环(想法是你想在大多数情况下避免递归):

      unsigned int numOfDigits(unsigned int num){
          if(num == 0) { return 1; }
          unsigned int size = 0;
          while(num != 0){
              size++;
              num /= 10;
          }
          return size;
      }
      

      这也可以通过引用返回值调用来完成。但是,如果没有必要,这有点奇怪。

      您甚至可以通过使用数学使其成为单线,cout &lt;&lt; floor(log_10(num)) &lt;&lt; endl;(如果 num 为零,则不考虑特殊情况)

      一种可行的解决方案是将 size 设为全局变量。但是,我建议不要这样做。一般应避免使用全局变量。只是想在别人推荐的时候提一下。

      最后,您可以使用一个类,例如 DigitsCounter,它通常是短暂的,并且对方法进行递归调用,其中 size 变量是类成员。不过,在你的情况下,这太过分了。

      【讨论】:

        【解决方案8】:

        试试这个代码:

        int numOfDigits(unsigned int num)
        {
            int size = 1;
            if (num!=0)
            {
                if (num / 10 != 0)
                   size+= numOfDigits(num / 10);
            }
            return size;
        }
        

        在主函数中使用返回值

        【讨论】:

          【解决方案9】:

          在递归函数上使用void 返回类型的问题在于嵌套调用不可能将任何信息返回给调用它们的方法。如果我们想在第一次(外部)调用时打印值,那么第一次调用无法从它的递归调用中获取任何信息。

          正如您所观察到的,我们不可能简单地打印值,因为从递归调用到输出字符之间没有一对一的映射(如果我们使用 Base 10...见附录)。如果我们放宽if 条件必须立即返回的条件,我们可以通过将信息作为参数传递给递归调用,然后在最深的递归级别打印值来避免这个问题。例如:

          // The accumulator starts at 0, then increases by 1 for each recursive call
          void numOfDigits(unsigned int num, unsigned int accumulator = 0) {
            if (num == 0) {
              if (accumulator == 0) cout << 1; // Special case: numOfDigits(0) = 1
              else cout << accumulator;
          
            } else numOfDigits(num / 10, accumulator + 1);
          } 
          

          旁注:现在这是一个尾递归方法:它将递归调用作为它的最后一个动作。这很有用,因为它允许编译器降低方法的空间复杂度(参见explanation)。本质上,编译器将方法转换为简单的迭代,无需累积堆栈帧。

          附录: 我能看到保持约束 (2) 的唯一方法是打印 Base 1 中的位数,因为从参数中获取的每个数字直接对应于 Base 中的单个字符1)。这可能不是您的意思,但这里有一个解决方案:

          // The accumulator starts at 0, then increases by 1 for each recursive call
          void numOfDigits(unsigned int num) {
            if (num == 0) return;
          
            else {
              cout << 1
              numOfDigits(num / 10);
            }
          }
          

          注意这个方法不会为numOfDigits(0)打印任何东西,如果我们想保留if (num == 0) return;这一行,这是必要的

          【讨论】:

            【解决方案10】:
            • numOfDigits 是一个空函数

            • “if”条件为 if(num==0), return

            • “else”将调用递归。

            这里:

            void numOfDigits(unsigned int num)  {
                if (num == 0)
                    return;
            
                // The "else" will call the recursion.
                else {
            
                    static int size = 1;
            
                    if (num / 10 != 0) {
                        size++;
            
                        numOfDigits(num / 10);
                    }
            
                    else {
                        cout << size << '\n';
                        size = 1;
                    }
                }
            }
            

            【讨论】:

            • 如果您多次调用该函数,则会给出错误答案。
            • @PeteBecker 试试看。
            • 哦,我明白了:size 最后被重置。嘘。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-11-25
            • 1970-01-01
            • 2014-06-08
            • 2019-07-04
            • 2015-07-28
            相关资源
            最近更新 更多