【问题标题】:C: Print only not common elements in 2 arraysC:仅打印 2 个数组中不常见的元素
【发布时间】:2016-12-09 17:18:40
【问题描述】:

我有一些 C 代码我想修改非常简单。

假设我有两个这样的数组

   int v1[5] = {1, 3, 7, 13, 10};
   int v2[2] = {1, 10};

我想打印不常见的元素(差异),例如:

3, 7, 13

这是我的尝试,但还不够:

#include <stdio.h>

int main()
{
    int v1[5] = { 1, 3, 7, 13, 10 };
    int v2[2] = { 1, 10 };

    for (int i = 0; i < sizeof(v1) / (sizeof * v1); i++) {
        for (int j = 0; j < sizeof(v2) / (sizeof * v2); j++) {
            if (v1[i] != v2[j]) {
                printf("%d ", v1[i]);
                break;
            } else {
                break;
            }
        }
    }

    return 0;
}

这两个数组总是很短(最多 6 个元素)。他们没有订购,我不应该修改它们。它们中的每个元素都是唯一的,即每个数字在每个数组中只能出现 1 次。 v2 将仅包含 v1 中元素的子集。 实现这一目标的最有效方法是什么?

【问题讨论】:

  • 首先,if(){}else{} 语句中的break 语句是什么?无论如何你执行它。你需要重新设计循环,break 完全错误。
  • 作为第二次优化,从 for() 语句中取出 sizeof(v1) / (sizeof * v1) 和类似的语句:它执行每个周期并且它是恒定的。
  • @nopasara:sizeof 表达式在编译时进行评估并折叠,以便程序看到 2 和 5。不过,您的建议对于在字符串的循环中调用 strlen 很有用。
  • @M Oehm:我真的不知道 sizeof() 将如何评估,也不知道在编译期间如何处理除法,这取决于编译器。它只是一个古老的优化规则:尽可能早地计算一次常量值。我会这样做以防万一,不会有错误的地方。
  • 元素的取值范围是多少?小数字还是整个[INT_MIN...INTMAX] 范围?

标签: c array-difference


【解决方案1】:

一种在内存方面贪婪但在 CPU 周期(线性时间)方面快速的方法是直方图,因为一般意义上的列表比较通常使用二次执行复杂度 :(

代码清单


#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>

int main(void) {

    /* Allocate. */
    int numElements1 = 0;
    int numElements2 = 0;

    const int maxHistVal = UINT8_MAX + 1;
    const int maxElements = 10;
    const int minElements = 1;
    uint8_t *arr1 = NULL, *arr2 = NULL;
    uint8_t *histogram = NULL;

    /* Init random seed. */
    srand(time(NULL));

    /* Determine number of elements for each array. */
    numElements1 = (rand() % (maxElements - minElements)) + minElements;
    numElements2 = (rand() % (maxElements - minElements)) + minElements;

    /* Generate two random arrays with non-duplicated values. */
    if (NULL == (arr1 = calloc(numElements1, sizeof(uint8_t)))) {
        return ENOMEM;
    } else if (NULL == (arr2 = calloc(numElements2, sizeof(uint8_t)))) {
        free(arr1);
        return ENOMEM;
    } else if (NULL == (histogram = calloc(maxHistVal, sizeof(uint8_t)))) {
        free(arr2);
        free(arr1);
        return ENOMEM;
    } else {
        /* Have our sample arrays and histogram. Populate them and print them
         * out.
         */
        printf("ARR1: ");
        uint8_t j = 0;
        for (int i = 0, j = 0; i < numElements1; i++) {
            /* Populate array. */
            j += (rand() % 2) + 1;
            arr1[i] = j;
            printf("%-3d ", arr1[i]);
            /* Update histogram. */
            histogram[arr1[i]]++;
        }
        printf("\n");
        printf("ARR2: ");
        for (int i = 0, j = 0; i < numElements2; i++) {
            /* Populate array. */
            j += (rand() % 2) + 1;
            arr2[i] = j;
            printf("%-3d ", arr2[i]);
            /* Update histogram. */
            histogram[arr2[i]]++;
        }
        printf("\n");
        /* Print out only values that appear exactly once in the histogram. */
        printf("HISTOGRAM: UNIQUE VALUES: ");
        for (int i = 0, j = 0; i < maxHistVal; i++) {
            /* Print histogram. */
            if (1 == histogram[i]) {
                printf("%-3d ", i);
            }
        }
        printf("\n");
        /* For fun, identify the duplicates. */
        printf("HISTOGRAM: DUPLICATE VALUES: ");
        for (int i = 0, j = 0; i < maxHistVal; i++) {
            /* Print histogram. */
            if (1 < histogram[i]) {
                printf("%-3d ", i);
            }
        }
    }

    /* Cleanup..*/
    free(histogram);
    free(arr2);
    free(arr1);

    return 0;
}

样品运行


ARR1: 2   3   4   6   8   9   10  
ARR2: 1   2   3   4   
HISTOGRAM: UNIQUE VALUES: 1   6   8   9   10  
HISTOGRAM: DUPLICATE VALUES: 2   3   4  

【讨论】:

  • 对于具有小范围可能值的数组来说,这是一种很好的方法,它可以一次性为您提供交集、差异和联合(未显示)。
  • @MOehm 谢谢!我在本科和硕士期间做了很多数学,并且喜欢在适当的时候做基于统计的模型。哈希表或稀疏矩阵可以容纳更广泛的数据类型,但在这一点上并没有那么快。
  • 你可以写两个函数,更少的代码,更少的错误。您在此处声明/定义变量的注释“/* Allocate. */”是错误的。另外,你应该只在使用变量时声明它,int numElements1 = (rand() % (maxElements - minElements)) + minElements;。在 for 循环中,, j = 0 没用。您不应该return ENOMEM;,因为某些值是保留的,请在 main 中使用 0 或 1。
  • @Stargateur DEAD WRONG #1 如果我的代码故意被编写为符合 C89 标准,那么您关于我应该何时分配的声明是错误的,除非我创建了一个新的块范围,而 LINT 会尖叫。 DEAD WRONG #2: 第二个for 循环中的j=0 确实是必要的。 WRONG #3: 返回值取决于实现。只有exit(2)需要10,即:EXIT_SUCCESSEXIT_FAILUREPEDANTIC #4: 是的,我在顶部声明/定义/两者。注释是为了指出最初的代码行是留出变量供以后使用。
  • @DevNull 我认为您需要将 C 写成“K&R C”投诉,这是最好的版本。顺便说一句,我赞成你的回答。但是你的风格很旧。而且,为什么你认为退出通知只有两个宏?
【解决方案2】:

实现这一目标的最有效方法是什么?

如果a[], b[] 中的值范围限制在 0 到 63,代码可以使用 unsigned long long 掩码。

这将遍历每个数组 l1 + l2 操作,而不是带有 l1 * l2 操作的双 for() 循环。

#include <assert.h>
#include <stdio.h>

int main(void) {
  const int v1[5] = { 1, 3, 7, 13, 10 };
  const int v2[2] = { 1, 10 };

  unsigned long long mask = 0;
  for (size_t i = 0; i < sizeof(v2) / (sizeof *v2); i++) {
    assert(v2[i] >= 0 && v2[i] < 64);
    mask |= 1ull << v2[i];
  }
  mask = ~mask;
  for (size_t i = 0; i < sizeof(v1) / (sizeof *v1); i++) {
    assert(v1[i] >= 0 && v2[i] < 64);
    if ((1ull << v1[i]) & mask) {
      printf(" %d", v1[i]);
    }
  }
  puts("");
  return 0;
}

输出

 3 7 13

【讨论】:

  • 数组的元素值在问题中不受限制。
  • 如果你想要一个无符号的 64 位,最好使用 uint64_t。
  • @Stargateur uint64_t 是个好主意。由于unsigned long long 至少是 64 位的,这将起作用,我避免一次将 OP 引入太多新想法。注意:uint_least64_tuint64_t 更便携。当然可以使用 uint_max_t 来获得最大范围 [0...CHAR_BIT*sizeof(uint_max_t)) 与此方法。
  • @chux 我找不到 uint_least64_t 和 uint64_t 之间的区别。你能解释一下吗?
  • @Stargateur uint64_t 是一种正好 64位的类型。 uint_least64_t 是一种 至少 64 位的类型(也没有填充)。很少有机器没有 2 的整数宽度。所有符合 C99/C11 的编译器都实现 uint_least64_t几乎所有编译器/平台也实现了可选 uint64_t, uint32_t, uint16_t, uint8_t 类型。你可能永远不会为不支持uint64_t的机器编写代码。
【解决方案3】:

您可以首先获取任何数组并逐个元素迭代它,并通过嵌套的 for 循环查找该元素是否也在第二个数组中,并将 if 条件放在内部 for 循环中,并将公共元素存储在另一个数组中,然后然后将这两个数组与该数组一一进行比较,并将不常见的元素放入另一个数组中。

喜欢:

int a[min(l1,l2)], b[l], x = 0, k = 0, flag = 1;
for(int i=0; i<l1; i++){
for(int j=0; j<l2; j++){
    if(v1[i]==v2[j]){
        a[k] = v1[i];
        k++;
    }
}
}
for(int i=0; i<l1; i++){
flag = 1;
for(int j=0; j<k; j++){
    if(v1[i] == a[j]){
      flag = 0;
      break;
    }
}
if(flag==1){
    b[x] = v1[i];
    x++;
}
}

for(int i=0; i<l2; i++){
flag = 1;
for(int j=0; j<k; j++){
    if(v2[i] == a[j]){
      flag = 0;
      break;
    }
}
if(flag==1){
    b[x] = v2[i];
    x++;
}
}

之后就可以打印数组了。

【讨论】:

  • 这仅适用于示例数组,其中v1 ⊃ v2。如果您更改数组以使v2 中的元素不在v1 中,您将看到您的代码遗漏了它们。换句话说,您的代码计算不对称差异v1 - v2,但我认为OP 正在寻找symmetric difference
  • 应该在发布之前检查您的代码。或者有足够的信心发布未经测试的代码。
  • (但是不,它不起作用。不是直接计算差异v1 - v2,而是先确定交集i = v1 ∩ v2,然后计算v1 - i来计算它,同理结果。)
  • 我不会在这里与您争论,因为显然这不会导致任何结果。但是,正如您接受和两次赞成的答案一样,既错误又糟糕。
  • 建议格式化代码以改善其表现。
【解决方案4】:
#include<stdio.h>

#define MAX_OLD   4
#define MAX_NEW   2
int main()
{
   int old[] = {3, 4, 5, 6};
   int new[] = {4, 5};
   int temp[] = {0, 0, 0, 0, 0, 0, 0, 0};
   int j = 0,z=0,i=0;
   for(i = 0; i < MAX_NEW; i++)
   {
      for(; j < MAX_OLD; j++)
      {
         if(old[j] <= new[i])
         {
            if(old[j]!=new[i])
               temp[z++] = old[j];
         }
         else
         {
            break;
         }
      }
   }
   while(j<MAX_OLD)
      temp[z++]=old[j++];
   printf("Temp[0] : %d  %d %d %d %d %d %d %d\n",temp[0],temp[1],temp[2],temp[3],temp[4],temp[5],temp[6],temp[7]);
   return 0;
}

【讨论】:

    猜你喜欢
    • 2012-09-25
    • 2020-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多