使用O(n)运行时查找范围内的元素

时间:2019-02-02 08:53:22

标签: c

我试图写来自用户的接收与大小“N”的阵列的功能,具有值0之间---> N-1的函数应该返回“1”,如果所有的值之间0 --- - > N-1是否有否则返回0的我们可以假定数字该用户将输入仅有效的值。在0 ----> N-1之间。

示例:N = 5,值:2,1,4,0​​,3 ---->返回1,          N = 5,值:2,3,4,0,3 ---->返回0

我尝试了各种方法来解决此问题。

想过一个因子,但发现有很多方法可以让使用重复的号码和唯一编号相同的阶乘。也考虑过对数字求和,但是仍然有太多方法可以得到相同的答案。有什么办法可以确保我只有唯一的项目而没有子数组?

我们不能使用子数组(另一个计数器数组等),并且该功能应运行O(n)。

3 个答案:

答案 0 :(得分:4)

如果允许您修改输入数组,则可以在O(N)中解决该问题。

观察:

  1. 如果对数组进行排序,则问题将不复存在。

  2. 对值也为0 ... N-1的数组0 ... N-1进行排序也很简单,由于每个元素的位置都是其值,因此可以迭代一次,将元素交换到其最终位置

在交换期间仅需要额外检查一下,位置 i 上的元素尚未具有值 i ,这意味着 i 在数组中出现两次。

int check(unsigned* a, unsigned size) {
    for (unsigned i = 0; i < size; ++i) {
        unsigned b = a[i];
        if (b != i) {
            do {
                if (b < 0 || b >= size)
                    return false; // value out of range
                unsigned c = a[b];
                if (b == c)
                    return false; // duplicate value
                a[b] = b;
                b = c;
            } while (b != i);
            a[i] = i;
        }
    }
    return true;
}

请注意,内部循环使解决方案看起来为O(N 2 ),但事实并非如此-每个元素最多访问两次。需要内部循环来解决循环,如{1,2,3,0}一样。

答案 1 :(得分:3)

这是我的版本。它在O(n)中运行。

这个想法是要操纵原始数组并向其添加N以便标记已遇到的所有值。然后我们进行一次扫描,检查所有值均大于或等于N,然后将其更改回原始值。

唯一的警告是数组必须是可变的。

#include <stdio.h>

int check(unsigned* a, size_t size) {
  for (size_t i = 0; i < size; ++i) {
    if (a[i] >= size) {
      return 0;
    }   
  }
  for (size_t i = 0; i < size; ++i) {
    size_t const x = a[i] % size;
    a[x] = x + size;
  }
  int result = 1;
  for (size_t i = 0; i < size; ++i) {
    if (a[i] < size) {
      result = 0;
    } else {
      a[i] = a[i] - size;
    }   
  }
  return result;
}


int main() {
  unsigned a1[] = {0,5,1,3,2,4};
  unsigned a2[] = {0,5,1,3,0,0};
  printf("a1: %d\n",check(a1,sizeof(a1)/sizeof(*a1)));
  printf("a2: %d\n",check(a2,sizeof(a2)/sizeof(*a2)));
}

答案 2 :(得分:2)

所有的值都是正的,所以我们可以利用我们的目的位标志。

迭代阵列;对于每个元素,请检查它是否为负,是否为负,然后减去1。如果它在有效范围[0,N-1]之外,则输入数组当然是无效的,尽管您说我们不必担心这个。

如果在范围内,则将其用作数组本身的索引;如果找到的值为正,则使其为负,然后减去1。如果值为负,则意味着存在重复的元素(您已经对其进行了符号交换)。

(以下简称“减去1”的是要考虑的0,其保持相同否定时)

由于信鸽原理,如果您到达的最后一个元素没有重复且没有超出范围,则输入数组将仅包含[0,N-1]范围内的所有元素。如果您对将数组都保留为负数感到不满意,则可以进行最后遍操作以翻转每个数字的符号。

bool check(int *arr, int N) {
    bool ret = true;
    for(int i = 0; i < N && ret; ++i) {
        int v = arr[i];
        if(v < 0) v = -v - 1;
        if(v >= N || arr[v] < 0) ret = false;
        else arr[v] = -arr[v] - 1;
    }
    for(int i = 0; i < N; ++i) { 
        if(arr[i] < 0) arr[i] = -arr[i] - 1;
    } 
    return ret;
}