【问题标题】:A quick way to test whether all array elements are zero测试所有数组元素是否为零的快速方法
【发布时间】:2021-04-12 14:37:29
【问题描述】:

TL;DR 我想知道如何清理第一个 if 语句。我尝试在网上查找,但一无所获。

我正在编写一个程序来测试用户输入的数字是否有重复数字。我设法创建了一个 10 元素布尔数组 (a[10]),如果 a[i] 等于 0,这意味着数字“i”最多出现在键入的数字中一次。如果 a[i] 等于 1,则数字“i”在键入的数字中至少出现两次(因此重复)。注意 0

现在我试图分析这个数组中的值,如果所有值都为零,那么我们输入“重复数字”。如果不是,我们会说哪些数字是重复的。

if(a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 && a[5] == 0 && a[6] == 0 && a[7] == 0 && a[8] == 0 && a[9] == 0)  
       printf("No repeated digits");
  
else  
  printf("Repeated digits: "); 
  for(i = 0; i < 10; i++) {
        if(a[i] == 1)
        printf("%d ", i); 
    }   


我找不到将 for 循环与 if 循环结合使用来清理第一个 if 语句的方法。我已经尝试在网上查找,但找不到解决方案。

【问题讨论】:

  • “快速”在运行时?或者最少的代码?
  • 快速,因为不必显式输入每个数组元素等于零。尝试使用某种循环机制等效地写出第一个 if 语句。想象一下,如果这个问题是以 99 为底而不是以 10 为底! (!为了效果,而不是阶乘)
  • 如果您将“数组”作为位向量存储在单个无符号整数中,您可以通过一次测试检查所有位是否为零:a == 0
  • 首先创建数组时,如果任何元素不为零,请设置一个标志。
  • 我需要看看这个标志的东西,我第一次听说它,听起来很有用。

标签: c


【解决方案1】:

有一个技巧可以检查数组是否所有元素都等于 N:

if (a[0]==N && memcmp(a, a+1, (array_length-1)*sizeof(a[0]) ) == 0)
    printf("All equal to N\n");

在你的情况下,你可以这样做:

if (a[0]==0 && memcmp(a, a+1, 9*sizeof(a[0]) ) == 0)
    printf("All zeros\n");

此代码明确检查第一个元素是否为零,然后memcmp 正在为您执行以下检查:

a[0] == a[1] && a[1] == a[2] &&....

这不需要像其他基于 memcmp 的答案那样额外分配和初始化零数组。

【讨论】:

    【解决方案2】:

    或使用:

    for (i=0; i<10; i++)
        if (a[i])
            break;
    
    if (i==10) printf("No repeated digits");
    else {
        //...
    }
    

    【讨论】:

    • 这个答案很好地不依赖于 a 的元素有一个零位模式,它只是与 0 比较吗?
    【解决方案3】:

    您可以使用标志来指示是否找到了非零元素。

    int nonzero_found = 0;
    
    for(i = 0; i < 10; i++) {
        if (a[i] != 0) {
            nonzero_found = 1;
            break;
        }
    }
    
    if (nonzero_found) {
        printf("Repeated digits: "); 
        for(i = 0; i < 10; i++) {
            if(a[i] == 1)
                printf("%d ", i); 
        }
    } else {
        printf("No repeated digits");
    }
    

    或者如果你真的想打印Repeated digits: ,即使没有重复的数字(比如你​​的原始代码):

    int nonzero_found = 0;
    
    for(i = 0; i < 10; i++) {
        if (a[i] != 0) {
            nonzero_found = 1;
            break;
        }
    }
    
    if (!nonzero_found) {
        printf("No repeated digits");
    }
    
    printf("Repeated digits: "); 
    for(i = 0; i < 10; i++) {
        if(a[i] == 1)
            printf("%d ", i); 
    }
    

    【讨论】:

    • 这个标志的东西很有用。谢谢。
    • 不需要if (nonzero_found) { printf("Repeated digits: "); for(i = 0; i &lt; 10; i++) {i = 0。从i 当前值继续。
    【解决方案4】:

    您可以将memcmp 与设置为所有元素为零的相同类型的复合文字结合使用。

    假设a 的元素是int 类型:

    if (memcmp(a, (int[10]){0}, sizeof a) == 0) {
      printf("all zero\n");
    } else {
      printf("not all zero\n");
    }
    

    【讨论】:

    • 它是一个 10 元素的布尔数组。布尔填充可能不为零吗?
    • 假设零数组的大小应该动态设置而不是固定为10,那么使用复合文字作为 VLA 可能不是最好的主意,因为这可能会导致堆栈分配缓慢。我完全相信每个编译器都足够聪明,可以将本地的全零 VLA 识别为它实际上不需要分配的东西。如果代码需要通用,则大小为“max”的static const 数组可能会更快。无论如何都必须拆开。
    • @tstanisl 没有,直到您想将代码变成通用代码而不是使用硬编码的大小。
    【解决方案5】:

    对于初学者,您可以编写一个函数来计算一个数字在一个数字中出现的次数。

    如果函数接受有符号整数,那么您需要正确处理这些数字。

    这是一个演示程序。

    #include <stdio.h>
    
    #define N   10
    
    struct Digits
    {
        char a[N];
    };
    
    struct Digits split_to_digits( long long int n )
    {
        const long long int Base = N;
        
        struct Digits digits = { 0 };
        
        do 
        {
            ++digits.a[n < 0 ? -( n % Base ) : n % Base];
        } while ( n /= Base );
        
        return digits;
    }
    
    int main(void) 
    {
        while ( 1 )
        {
            printf( "Enter an integer number (0 - exit): " );
            
            long long int n;
            
            if ( scanf( "%lld", &n ) != 1 || n == 0 ) break;
            
            struct Digits digits = split_to_digits( n );
            
            int unique = 1;
            
            for ( size_t i = 0; unique && i < N; i++ )
            {
                unique = digits.a[i] < 2;
            }
            
            if ( unique )
            {
                puts( "No repeated digits." );
            }
            else
            {
                printf( "Repeated digits: " );
                
                for ( size_t i = 0; i < N; i++ )
                {
                    if ( !( digits.a[i] < 2 ) )
                    {
                        printf( "%zu ", i );
                    }
                }
                
                putchar( '\n' );
            }
        }
    
        return 0;
    }
    

    它的输出可能看起来像

    Enter an integer number (0 - exit): 12345
    No repeated digits.
    Enter an integer number (0 - exit): -12345
    No repeated digits.
    Enter an integer number (0 - exit): 12233445
    Repeated digits: 2 3 4 
    Enter an integer number (0 - exit): -12233445
    Repeated digits: 2 3 4 
    Enter an integer number (0 - exit): 0
    

    【讨论】:

      【解决方案6】:

      我对循环的建议:

      char const* prefix = "No repeated digits";
      
      for (i = 0; i < 10; ++i) {
          if (a[i] == 1 ) {
              // check if the prefix has been printed yet
              if (strcmp( prefix, "") != 0) {
                  printf("Repeated digits: ");
                  
                  // and set the prefix to nothing so it won't be printed again
                  prefix = "";
              }
      
              printf("%d ", i);
          }
      }
      
      printf( "%s\n", prefix);
      

      prefix 变量既可用作各种标志,也可用作要打印的文本行的前缀。

      这种方法的一个小好处是数组只被遍历一次。没什么大不了的(“Big O”复杂性没有改变),但也不是什么都没有。

      请注意,此代码假定 a[] 元素仅为 0 或 1,这是原始帖子中的代码暗示但未明确说明的。

      【讨论】:

      • 次要:if (!strcmp( prefix, "") {(缺少))看起来可以简化为if (prefix[0] == 0) {
      【解决方案7】:

      您可以为该任务创建一个函数。逻辑有点像标志方法,优点是您可以轻松地检查和重新检查程序不同位置的不同数组。

      函数类似于

      int allZeros( int *a, int len ) {
      
          int i = 0;
          
          while( i++ < len && !*a++ );
          
          return i == len+1;
      }
      

      并且可以这样使用:

      if( allZeros(a,10) ) puts("No repeated digits");
      
      else {
          printf("Repeated digits: ");
          etc...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-05
        • 1970-01-01
        • 2014-01-23
        • 2012-12-16
        • 2023-02-10
        • 2017-11-01
        相关资源
        最近更新 更多