【问题标题】:Function in C that realize the mathematical union of two arrays of int in another one [closed]C中实现两个int数组在另一个数组中的数学联合的函数[关闭]
【发布时间】:2014-12-30 21:51:07
【问题描述】:

语言是 ANSI C。我有 2 个 int 数组:ABA 有一个名为 m 的索引,B 有一个名为 n 的索引。分配说m 必须不同于n,所以数组必须有不同的大小。我已经对此进行了编码。 A 按升序排列,B 按降序排列。我必须编写一个函数,在另一个名为C 的数组中进行两个数组的数学联合。如果一个元素在两个数组中,您只需将一个元素放在联合数组中(数组C)。

我的代码不能很好地工作。最后一个元素没有排序,我收到一个带有非常大的最后一个数字的输出,我不知道它来自哪里。

int index_c=index_m+index_n; //the index of array c
// is obtained by the sum of two indexes of the array A and B
int c[index_c];
int k=0;
for (i=0; i < index_m; i++)
{   
    for (j=0; j < index_n; j++)
    {    
        if (a[i]==b[j])
        {
            c[k]=a[i]; //put only one time if is repeated more time in the two arrays
        }
        else 
        {
            c[k]=a[i]; //put the a[i] element in the array c
            c[k+1]=b[j]; //the element of the other array next to
        }

    }
    k++;
}

printf("Elements in array C are: \n");
for (i=0; i<index_c; i++)
    printf("element %d\n", c[i]);

数组C不排序也没关系,我会在union之后排序。有什么建议吗?

当我添加 1 个输入时,我正在尝试 put k++ 的建议,当我将两个输入添加到数组 C 时,我尝试使用 k+2。现在它工作得有点好,但它不能完全工作。我的意思是在输出中我没有大数值,但输出值之一(第三个)与第一个相同。 示例:3 9 3 2 5 第二个 3 是错误的,它缺少一个被第二个 3 覆盖的数字。 其他示例 2 4 2 1 9

【问题讨论】:

  • A = 1 3 5 8 13B = 5 4 2 0的期望是什么?
  • 最好的代码应该对两个数组进行一次遍历。您知道两个数组都是有序的,尽管方向相反,因此您应该在简单的合并过程中从头到尾搜索一个,从头到尾搜索另一个。它还按排序顺序为您提供输出数据(如果您愿意,可以在升序和降序之间进行选择)。这为您提供 O(N) — 线性 — 时间,而不是您的骨架代码显示的二次 O(N*N) 时间。总是在可用时利用排序——它可以大大提高性能。
  • @Jongware:描述说 A 是按升序排列的,B 是按降序排列的。代码没有利用这一点。
  • @JonathanLeffler:是的,但从表面上看,代码没有利用 任何东西 :)(是的,我一定错过了)。
  • 只给c[0]c[kindex_m] 一个值。 c 的其余部分未初始化。

标签: c arrays algorithm sorting


【解决方案1】:

我发现了两个直接的逻辑错误,至少应该修复:

  1. 您要么在c 中存储一个数字,当两个输入相同时,然后将k 增加1,您存储两个数字进入c。然后,您也应该将 k 增加 2。在您现在拥有的代码中,您只需添加另一个 +1 - 但考虑将这些添加内容放在 if..else 测试块中以清楚起见。目前,您正在覆盖最后存储的内容。

  2. 您打印从 0 到 index_c 的结果,即两个输入数组的长度之和。这是不合逻辑的,因为你在扔数字。因此,您会得到“随机”数字作为输出;这些只是未初始化,即从未写入。打印从 0 到 k,因为 是您输入的有效范围。

【讨论】:

  • 我理解了第二点,所以我修改了printf,现在for从0变成了k。我认为没关系。我没有理解第一点;我错了,因为如果输入中的数字相同,我将一个数字存储在 c 中,并且...?也许你的意思是我必须在此之后将k 增加 1?而当我存储两个数字时,我必须将 k 增加两次?我尝试将您的建议放在 if...else 中。
  • 当你存储 1 个值时,将 k 增加 1。所以要存储 2,将其增加 2。将其视为“存储一个值”... 两次
  • for (i=0; i &lt; index_m; i++) { for (j=0; j &lt; index_n; j++) { if (a[i]==b[j]) { c[k]=a[i]; //put only one time if is repeated more time in the two arrays k++ } else { c[k]=a[i]; //put the a[i] element in the array c c[k+1]=b[j]; //the element of the other array next to k=k+2; } } k++; } printf("Elements in array C are: \n"); for (i=0; i&lt;k; i++) printf("element %d\n", c[i]); 对吗?
  • 不!您在正确的位置有k+2,但它仍然会在它之后执行k++在当前位置更改为k+1,或者按照我的建议,将k++ 移动到if 的第一部分。
【解决方案2】:

到目前为止,没有一个答案利用数组都已排序这一事实。这是一个几乎与 cmets 中建议的 merge 相同的实现。合并的复杂度是 O(m + n)。

我假设每个数组都没有重复项(没有 [0, 1, 1, 3]),但如果我假设错误,您可以添加像 if (k == 0 || k &gt; 0 &amp;&amp; C[k - 1] != A[i]) 这样的检查来解决这个问题。

函数返回C的长度。C按升序排列。要将C 按降序排序,请将if (A[i] &lt; B[j]) 更改为if (A[i] &gt; B[j])

int union_merge(const int *A, int m, const int *B, int n, int *C) {
  int i = 0, j = n - 1, k = 0;  

  while (i < m && j >= 0) {
    if (A[i] < B[j]) {
      C[k++] = A[i++];
    } else if (A[i] == B[j]) {
      C[k++] = A[i++];
      --j;
    } else {
      C[k++] = B[j--];
    }
  }

  while (j >= 0) {
    C[k++] = B[j--];
  }

  while (i < m) {
    C[k++] = A[i++];
  }

  return k;
}

【讨论】:

  • 耶!谢谢你。我正要发布一个与此相当的答案。请注意,将两个尾随 while 循环内联编写实际上更简单;其中只有一个(最多)会执行循环体——如果a 中的最后一个条目等于b 中的第一个条目,则两者都不会执行。
  • @JonathanLeffler 现在看起来干净多了 :) 谢谢。
【解决方案3】:

假设您有两个数组 A 和 B,以及联合数组 C。您可以将数组 A 和 B 输入到一个数组中。然后,您可以对该数组进行排序,并在对数组进行排序迭代后,如果尚未添加该值,则将值添加到数组 C(union array) 中。总复杂度为 O( N * log(N) ) 查看代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX 100000

int a[2*MAX+3], c[2*MAX+3];

int cmp(const void *a, const void *b) {
  if ( *(int*)a <  *(int*)b ) return -1;
  if ( *(int*)a == *(int*)b ) return 0;
  if ( *(int*)a >  *(int*)b ) return 1;
}

int main() {

  int i, k;

  int n, m; scanf("%d%d", &n, &m); // size of the first array and size of the second array
  n += m;
  for(i = 0; i < n; ++i) // O(N) , input both arrays into one array
    scanf("%d", &a[i]);

  qsort(a, n, sizeof(int), cmp); // O( N * log(N) ), sort the given array

  c[0] = a[0];
  for(i = 1, k = 1; i < n; ++i) // O(N)
    if(c[k - 1] != a[i]) // if the last element that you added to the union array is different than the current element in first array then add that element to union array
      c[k++] = a[i];

  for(i = 0; i < k; ++i) // O(K)
    printf("%d ", c[i]);
  return 0;
}

【讨论】:

  • 请注意,如果整数的值足够大且符号不同,您的比较器函数可能会溢出。这会导致未定义的行为。 (例如,如果一个条目包含 INT_MIN 和另一个 INT_MAX,则减法的结果是未定义的。)您实际上没问题的可能性很高,但这不是为完全通用编写比较器函数的好方法案例。
  • @JonathanLeffler 感谢您的通知,我会记住的。
  • 天哪:修改后的比较器是错误的。原始使用return (*(int *)a - *(int *)b); 并且适用于足够小的整数(如果要比较的每个整数的绝对值小于INT_MAX/2)。正确的比较比较器可能是:int cmp(const void *pa, const void *pb) { int a = *(int *)a; int b = *(int *)b; if (a &lt; b) return -1; else if (a &gt; b) return +1; else return 0; }。您当前的代码(如果a 指向的值小于b 指向的值,则return (*(int *a) &gt; *(int *)b); 永远不会返回负值。
  • 另请注意,C++ STL 库代码的比较器与 C 库 qsort()bsearch() 函数的比较器不同。 C++ 比较器确实只返回一个布尔值(松散地)*a &gt; *b(或者它可能是*a &lt; *b);比较器在比较代码中使用了两次以检查顺序。
  • @JonathanLeffler 感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 2018-04-27
相关资源
最近更新 更多