有效地找到整数,而不是大小为40、400或4000的集合

时间:2018-12-18 14:37:23

标签: c++ algorithm

与经典问题find an integer not among four billion given ones有关,但不完全相同。

通过 integers 进行澄清,我真正的意思只是其数学定义的一部分。也就是说,假设只有有限数量的整数。用C ++表示,它们是int范围内的[INT_MIN, INT_MAX]

现在给定std::vector<int>(无重复项)或std::unordered_set<int>,其大小可以为40、400、4000左右,但又不过大,如何有效地生成一个保证为不是给定的吗?

如果不必担心溢出,那么我可以将所有非零值相乘并将其乘积加1。但是确实存在。对手的测试用例可以仔细地包含INT_MAX

我更喜欢简单,非随机的方法。有吗?

谢谢!

更新:为消除歧义,我们假设未排序的std::vector<int>可以保证没有重复。所以我问是否有比O(n log(n))更好的东西。另外请注意,测试用例可能同时包含INT_MININT_MAX

6 个答案:

答案 0 :(得分:31)

您可以只返回输入中未包含的N+1个候选整数中的第一个。最简单的候选者是数字0N。这需要O(N)的空间和时间。

 int find_not_contained(container<int> const&data)
 {
     const int N=data.size();
     std::vector<char> known(N+1, 0);   // one more candidates than data
     for(int i=0; i< N; ++i)
         if(data[i]>=0 && data[i]<=N)
             known[data[i]]=1;
     for(int i=0; i<=N; ++i)
         if(!known[i])
             return i;
     assert(false);                     // should never be reached.
 }

随机方法可以提高空间利用率,但在最坏的情况下可能需要对数据进行更多的传递。

答案 1 :(得分:9)

随机方法在这里确实非常有效。

如果我们要使用确定性方法,并假设大小 n 不太大,例如4000,那么我们可以创建大小为{的向量 x {1}}(或更大一点,例如4096,以便于计算),以0初始化。

对于范围中的每个m = n + 1,我们只需设置x [array [i]模m] =1。

然后在 x 中进行简单的O(n)搜索将提供一个不在 array

中的值

注意:模运算不完全是“%”运算

编辑:我提到通过选择4096的大小使计算更容易。更具体地讲,这意味着模运算是通过简单的i操作执行的。

答案 2 :(得分:6)

如果允许使用以下算法对输入向量进行重新排序,则可以使用O(1)辅助空间找到O(N)时间中最小的未使用整数。 [注1](如果向量包含重复数据,该算法也将起作用。)

size_t smallest_unused(std::vector<unsigned>& data) {
  size_t N = data.size(), scan = 0;
  while (scan < N) {
    auto other = data[scan];
    if (other < scan && data[other] != other) {
      data[scan] = data[other];
      data[other] = other;
    }
    else
      ++scan;
  }
  for (scan = 0; scan < N && data[scan] == scan; ++scan) { }
  return scan;
}

第一遍保证如果在位置k之后找到了[0, N)范围内的某个k,那么它现在位于位置k。通过交换完成此重新安排,以避免丢失数据。扫描完成后,数组中任何地方都不会引用值与其索引不同的第一个条目。

该断言可能不是100%显而易见的,因为可以从更早的索引中引用一个条目。但是,在那种情况下,该条目不能是与其索引不相等的第一个条目,因为较早的条目将满足该条件。

要查看此算法为O(N),应注意,仅当目标条目不等于其索引时才发生第6行和第7行的交换,并且在交换之后目标条目相等到它的索引。因此,最多可以执行N交换,并且第5行的if条件最多是trueN。另一方面,如果if条件为假,则scan将递增,这也只能发生N次。因此,if语句最多被评估2N次(即O(N))。


注意:

  1. 我在这里使用了无符号整数,因为它使代码更清晰。该算法可以很容易地针对有符号整数进行调整,例如通过将[INT_MIN, 0)上的有符号整数映射到无符号整数[INT_MAX, INT_MAX - INT_MIN)上(减法运算是数学运算,不是根据C语义进行的,这不允许结果为在2的补码中,是相同的位模式。当然,这会改变数字的顺序,从而影响“最小的未使用整数”的语义;保留订单的映射也可以使用。

答案 3 :(得分:4)

使x等于(INT_MIN..INT_MAX),然后针对所有像素进行测试。在失败时测试x ++(对于40/400/4000,很少见)。

答案 4 :(得分:2)

步骤1: 对向量进行排序

那可以在O(n log(n))中完成,您可以在线找到一些不同的算法,最喜欢使用一种。

步骤2: 查找不在向量中的第一个int

轻松地从INT_MIN迭代到INT_MIN + 40/400/4000,检查向量是否具有当前int:

伪代码:

SIZE = 40|400|4000 // The one you are using
for (int i = 0; i < SIZE; i++) {
    if (array[i] != INT_MIN + i)
        return INT_MIN + i;

解决方案为 O(n log(n)+ n)含义: O(n log(n))


编辑:抱歉,请阅读您的编辑要求的内容比 O(n log(n))更好。

答案 5 :(得分:0)

对于在std::unordered_set<int>中提供整数(而不是std::vector<int>)的情况,您可以简单地遍历整数值的范围,直到遇到一个整数,即unordered_set<int>中不存在。在std::unordered_set<int>中搜索整数的存在非常简单,因为std::unodered_set确实提供了通过其find()成员函数进行搜索的功能。

这种方法的空间复杂度为 O(1)


如果以int(即std::numeric_limits<int>::min())的最低可能的值开始遍历,则将获得最低 {{ 1}}未包含在int中:

std::unordered_set<int>

类似地,如果您开始使用int find_lowest_not_contained(const std::unordered_set<int>& set) { for (auto i = std::numeric_limits<int>::min(); ; ++i) { auto it = set.find(i); // search in set if (it == set.end()) // integer not in set? return *it; } } (即int)的最大可能值遍历,则会获得最低 std::numeric_limits<int>::max()中未包含int

std::unordered_set<int>

假设哈希函数将int find_greatest_not_contained(const std::unordered_set<int>& set) { for (auto i = std::numeric_limits<int>::max(); ; --i) { auto it = set.find(i); // search in set if (it == set.end()) // integer not in set? return *it; } } 均匀地映射到int的存储桶中,则可以在unordered_set<int>上实现搜索操作在恒定的时间内。这样,运行时复杂度将为 O(M),其中 M 是您要查找的非整数值的整数范围的大小。 M unordered_set<int>的大小为上限(即您的情况 M <= 4000 )。

实际上,使用这种方法,选择任何大小大于unordered_set<int>的整数范围,都可以保证与unordered_set中不存在的整数值相抵。 / p>