快速测试数组所有元素是否为零的方法

4
TL;DR:我想知道如何简化第一个if语句。我在网上找不到相关信息。
我正在编写一个程序,测试用户输入的数字中是否有重复的数字。我成功创建了一个10个元素的布尔数组(a[10]),如果a[i]等于0,则表示数字'i'最多只出现一次。如果a[i]等于1,则表示数字'i'至少出现两次(因此重复)。注意0 <= i <= 9。
现在我正在尝试分析该数组中的值,如果所有值都等于零,则打印“Repeated digit”。如果不是,则说明哪些数字是重复的。
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语句。我已经尝试在网上寻找解决方案,但没有找到。

2
是指运行时间快?还是代码最少? - Eugene Sh.
快速,指的是不必显式地输入每个数组元素等于零。尝试使用某些循环机制等效地编写第一个if语句。想象一下,如果这个问题是在99进制而不是10进制中,会怎样!(感叹号只是为了效果,不是阶乘) - Guthrie
1
如果您将“数组”存储为单个无符号整数中的位向量,则可以通过单个测试检查所有位是否为零:a == 0 - Ian Abbott
在创建数组时,如果任何元素不为零,请设置一个标志。 - Eric Postpischil
我需要先检查一下这个标志的东西,这是我第一次听说它,听起来很有用。 - Guthrie
这回答解决了你的问题吗?C 语言中检查全零缓冲区的更快方法是什么? - thetaprime
7个回答

9

检查一个数组中所有元素是否都等于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 的答案一样。

4

或使用:

for (i=0; i<10; i++)
    if (a[i])
        break;

if (i==10) printf("No repeated digits");
else {
    //...
}

这个答案很好,不依赖于a的元素具有零比特模式的要素,它只是将其与0进行比较吗? - chux - Reinstate Monica

1
你可以使用一个标志来指示是否找到了非零元素。
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); 
}

这个标志很有用。谢谢。 - Guthrie
如果 nonzero_found 为真,则不需要 i = 0。只需从当前值 i 继续即可。代码如下:if (nonzero_found) { printf("Repeated digits: "); for(i = 0; i < 10; i++) { - chux - Reinstate Monica

0

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

假设a的元素是int类型:

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

这是一个包含10个布尔值的数组。布尔填充可能非零吗? - chux - Reinstate Monica
假设零数组的大小应该动态设置而不是固定为“10”,那么使用复合字面量作为VLA可能不是最好的选择,因为这可能会强制进行缓慢的堆栈分配。我并不完全相信每个编译器都足够聪明,能够将本地的全零VLA识别为实际上不需要分配的东西。如果代码需要通用性,则具有大小“max”的static const数组可能更快。无论如何,它都必须被反汇编。 - Lundin
@tstanisl 直到你想将代码变成通用的,而不是使用硬编码大小时,才会有一个。 - Lundin

0

我的循环建议:

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,这是原始帖子中的代码暗示但未明确说明的。


1
小问题:if (!strcmp( prefix, "") {(缺少一个 ))看起来可以简化为 if (prefix[0] == 0) { - chux - Reinstate Monica

0
你可以为此任务编写一个函数。这种逻辑有点类似于标志方法,但优势在于您可以轻松地检查和重新检查程序中不同位置的不同数组。
该函数将类似于:
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...
}

0

首先,您可以编写一个函数来计算数字在数字中出现的次数。

如果该函数接受带符号整数,则需要正确处理这些数字。

以下是一个演示程序。

#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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接