有效地找到一个不在40、400或4000尺寸的整数

Efficiently find an integer not in a set of size 40, 400, or 4000

本文关键字:4000尺 整数 一个 有效地      更新时间:2023-10-16

与经典问题有关的

在给定的四十亿个,但不是完全相同的整数。

通过 Integers 我真正的意思只是其数学定义的一个子集。也就是说,假设只有有限数量的整数。在C 中说,它们是[INT_MIN, INT_MAX]int

现在给出了std::vector<int>(无重复)或std::unordered_set<int>,其大小可以为40、400、4000左右,但不太大,如何有效地生成一个不在给定的数字?

如果不担心溢出,那么我可以将所有非零的产品乘以1。但是有。对手测试用例可能删除了INT_MAX

我更支持简单的非随机方法。有什么吗?

谢谢!

更新:要清除歧义,假设一个未分类的std::vector<int>保证没有重复。因此,我问是否有比O(n log(n))更好的东西。另请注意,测试用例可能同时包含INT_MININT_MAX。输入。最简单的候选人是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.
 }

随机方法可以更有效地有效,但在最坏情况下可能需要更多通过数据。

随机方法确实非常有效。

如果我们想使用确定性方法并假设大小 n 不太大,例如4000,那么我们可以创建一个大小 m = n + 1的vector x (或稍大一点,例如4096,以促进计算),以0。

初始化

对于范围内的每个i,我们只是设置x [array [i] modulo m] =1。

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

注意:Modulo操作不完全是"%"操作

编辑:我提到通过在此处选择4096的大小使计算变得更容易。要更具体,这意味着使用简单的&操作执行Modulo操作

如果允许使用以下算法重新排序输入向量,则可以使用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条件最多为true,最多为N次。另一方面,如果if条件为false,则scan会增加,这也只能发生N次。因此,if语句最多是2N次(是O(n))。


注意:

  1. 我在这里使用了未签名的整数,因为它使代码更清晰。可以轻松地针对已签名的整数调整算法,例如,通过将[INT_MIN, 0)签名的整数映射到未签名的整数[INT_MAX, INT_MAX - INT_MIN)(减法是数学的,而不是根据C语义,而不是根据C语义的表示。那是相同的位模式。当然,这会改变数字的顺序,这会影响"最小未使用整数"的语义;也可以使用保留订单的映射。

制作随机x(int_min..int_max)并对其进行测试。测试X 故障(40/400/4000的非常罕见的情况)。

步骤1: 对vector

排序

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

步骤2:在vector 中找到第一个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))更好的东西(n log(n)),对不起。

对于在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())的可能值,则您将获得std::unordered_set<int>中未包含的最低最低 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()),则将获得std::unordered_set<int>中不包含的最低 int最低> CC_43:

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 s是由哈希函数统一在unordered_set<int>的桶中映射的,则可以在恒定时间内实现unordered_set<int>上的搜索操作。然后,运行时复杂性将为 o(m),其中 m 是您正在寻找非邻近值的整数范围的大小。 m unordered_set<int>的大小上限(即,在您的情况下 m&lt; = 4000 )。

的确,使用这种方法,选择大小大于unordered_set大小的任何整数范围,可以保证会出现与unordered_set<int>中不存在的整数值。