如果数组中存在0到n-1范围内的所有元素,则函数返回0或1,运行时为O(n)

时间:2019-02-09 17:20:47

标签: c arrays

编辑: 我忘了提到我不想分配另一个临时数组。

我正在尝试解决C语言中的一个问题: 假设给定一个数组a,其大小为N。您知道该数组中的所有元素都在0到n-1之间。如果在(0到n-1)范围内缺少数字,则该函数应返回0。否则,它将返回1。如您所知,可以重复。问题是它应该在O(n)运行时上运行。 我想我设法做到了,但我不确定。从这里的旧文章来看,似乎几乎是不可能的,而且算法似乎比我拥有的算法复杂得多。因此,我感到有些不对劲。 我找不到您返回错误输出的输入。

无论如何,我们将不胜感激您的反馈,或者,如果您能想到可能不起作用的输入,这是代码:

int missingVal(int* a, int size)
{
  int i, zero = 0;
  for (i = 0; i < size; i++)
    //We multiply the element of corresponding index by -1
    a[abs(a[i])] *= -1;

  for (i = 0; i < size; i++)
  {
     //If the element inside the corresponding index is positive it means it never got multiplied by -1 
     //hence doesn't exist in the array
     if (a[i] > 0)
       return 0;

     //to handle the cases for zeros, we will count them
     if (a[i] == 0)
       zero++;
  }
  if (zero != 1)
    return 0;

  return 1;
}

4 个答案:

答案 0 :(得分:2)

您的程序可以工作并且位于O(N)中,但是它很复杂,最糟糕的是它会修改初始数组

可以就是这样:

int check(int* a, int size)
{
  int * b = calloc(size, sizeof(int));
  int i;

  for (i = 0; i != size; ++i) {
    b[a[i]] = 1;
  }

  for (i = 0; i != size; ++i) {
    if (b[i] == 0) {
      free(b);
      return 0;
    }
  }

  free(b);
  return 1;
}

答案 1 :(得分:2)

只需将值复制到另一个数组即可,将每个值放在其顺序位置。然后浏览副本以查看是否缺少任何内容。

答案 2 :(得分:2)

这个问题与相同一样,无法确定您的阵列是否重复。这就是为什么

  1. 数组中的所有数字都在0n-1之间
  2. 数组的大小为n

如果该范围内缺少数字,则仅表示另一个数字代替了它。这意味着数组必须有重复的数字

O(n)时间和O(1)空间中的算法

  1. 遍历数组
  2. 如果当前数字的符号为正,则使其为负
  3. 如果您发现负面消息,则表示您有重复项。由于所有项目本来都大于(或等于)0

实施

int missingVal(int arr[], int size)
{
    // Increment all the numbers to avoid an array with only 0s
    for (int i = 0; i < size; i++) arr[i]++;

    for (int i = 0; i < size; i++)
    {
        if (arr[abs(arr[i])] >= 0)
            arr[abs(arr[i])] = -arr[abs(arr[i])];
        else
            return 0;
    }

    return 1;
}

编辑

Bruno所述,如果我们有一个全为零的数组,那么我们可能会遇到问题。这就是为什么我在所有 edit 中增加所有数字的原因。

虽然这在算法中又添加了一个“ pass”,但解决方案仍然是 O(n)时间和O(1)空间

编辑#2

Bruno另一个优化此建议的好建议是,查看是否存在多个零而不是递增数组。

如果有2个或更多,我们可以直接返回0,因为我们发现了一个重复项(并且由于相同的原因,并不是该范围内的所有数字都在数组中)

答案 3 :(得分:0)

为克服排除任何额外内存消耗的要求,发布的算法只需简单地取反数组的值即可更改数组中的值,但这将使索引0保持不变。

我提出了一个不同的映射:从[0,size)到( -1-size -1 ],这样例如{0,1,2, 3,4,...}变为{-1,-2,-3,-4,-5,...}。请注意,对于整数的二进制补码表示,INT_MIN = -INT_MAX-1。

//  The following assumes that every value inside the array is in [0, size-1)
int missingVal(int* a, int size)      // OT: I find the name misleading
{
    int i = 0;
    for (; i < size; i++)
    {
        int *pos = a[i] < 0
            ? a + (-a[i] - 1)  // A value can already have been changed...
            : a + a[i];

        if ( *pos < 0 )        // but if the pointed one is negative, there's a duplicate
            break;

        *pos = -1 - *pos;
    }

    return i == size;         // Returns 1 if there are no duplicates 
}

如果需要,可以在返回之前通过一个简单的循环恢复原始值

if ( i != size ) {
    for (int j = 0; j < size; ++j) {
        if ( a[j] < 0 )
            a[j] = -a[j] - 1;
    }
} else {                             // I already know that ALL the values are changed
    for (int j = 0; j < size; ++j)
        a[j] = -a[j] - 1;
}