【问题标题】:Find the Smallest Integer Not in a List查找不在列表中的最小整数
【发布时间】:2009-10-19 03:44:45
【问题描述】:

我的一位同事使用的一个有趣的面试问题:

假设给定一个非常长的、未排序的无符号 64 位整数列表。您如何找到列表中没有出现的最小非负整数?

FOLLOW-UP:既然已经提出了通过排序的明显解决方案,你能比 O(n log n) 更快吗?

跟进:您的算法必须在具有 1GB 内存的计算机上运行

澄清:该列表位于 RAM 中,但可能会占用大量内存。预先给你列表的大小,比如 N。

【问题讨论】:

  • 我认为您可以省略非负部分,看看您是如何谈论无符号整数的。
  • 这个问题非常基本,除非我是国际海事组织的 waaaay off-base,但是,正如其他人所提到的,有一些问题要问,或者应该说明一些假设。
  • @paxdiablo:在这种情况下,说 O(n) 并没有那么大的意义。即使你将你的 2^64 位数组存储在复活节岛上的粘土片上并通过信鸽访问它,算法仍然是 O(n)。
  • 中途更改内存要求使这是一个很好的面试问题;-)
  • 我认为有趣的是所有答案都使用相同的通用解决方案(对数组进行排序并找到破坏序列的第一个值),但它们都使用不同的排序。 (修改后的快速排序,基数排序,...)接受的答案等同于丢弃 N 以上元素的计数排序。

标签: arrays algorithm


【解决方案1】:

如果数据结构可以就地变异并支持随机访问,那么您可以在 O(N) 时间和 O(1) 额外空间内完成。只需按顺序遍历数组,并且对于每个索引,将索引处的值写入由 value 指定的索引,递归地将该位置的任何值放置到其位置并丢弃值 > N。然后再次遍历数组寻找该位置其中 value 与索引不匹配 - 这是不在数组中的最小值。这会导致最多 3N 次比较,并且只使用几个值的临时空间。

# Pass 1, move every value to the position of its value
for cursor in range(N):
    target = array[cursor]
    while target < N and target != array[target]:
        new_target = array[target]
        array[target] = target
        target = new_target

# Pass 2, find first location where the index doesn't match the value
for cursor in range(N):
    if array[cursor] != cursor:
        return cursor
return N

【讨论】:

  • 小挑剔。您错过了一个小例子:当列表为 {0, ..., N-1} 时。在这种情况下,pass 1 什么都不做,而在 pass 2 中,array[cursor] == cursor 用于列表中的所有条目,因此算法不会返回。所以最后你需要一个'return N'语句。
  • 您的解决方案将域和范围混为一谈(目标既是值又是索引)。该范围受限于可用存储为 128M 个元素,但域大小为 2G。它将失败,因为单个条目的值大于可以分配到数组中的条目数。如果问题没有指定“非常长”,则答案很优雅,即使它破坏了输入。在这个问题中时空权衡非常明显,在提供的约束条件下可能无法获得 O(N) 的解决方案。
  • 第二遍可以使用二分查找代替线性查找。
  • 此解决方案仅在值的范围和索引具有可比性时才有效。
  • 它适用于较大的值。较大的值可以忽略,因为它们与不在数组中的最小值无关。对于您的示例,第一遍将遍历数组,忽略由于 target
【解决方案2】:

这是一个使用O(N) 空间的简单O(N) 解决方案。我假设我们将输入列表限制为非负数,并且我们希望找到不在列表中的第一个非负数。

  1. 查找列表的长度;假设它是N
  2. 分配N 布尔数组,初始化为所有false
  3. 对于列表中的每个数字X,如果X小于N,则将数组的X'th元素设置为true
  4. 从索引0 开始扫描数组,寻找false 的第一个元素。如果您在索引I 中找到第一个false,那么I 就是答案。否则(即当所有元素都是true)答案是N

实际上,“N 布尔数组”可能会被编码为“位图”或“位集”,表示为 byteint 数组。这通常会使用更少的空间(取决于编程语言),并且可以更快地完成对第一个 false 的扫描。


这就是算法工作的方式/原因。

假设列表中的N 数字不是不同的,或者其中一个或多个大于N。这意味着在0 .. N - 1 范围内必须有至少一个不在列表中的数字。所以寻找最小缺失数的问题必须因此归结为寻找最小缺失数的问题小于N。这意味着我们不需要跟踪大于或等于N 的数字......因为它们不会是答案。

上一段的替代方案是该列表是来自0 .. N - 1 的数字的排列。在这种情况下,步骤 3 将数组的所有元素设置为 true,步骤 4 告诉我们第一个“缺失”数字是 N


算法的计算复杂度为O(N),比例常数相对较小。它通过列表进行两次线性传递,或者如果列表长度已知以开始,则仅传递一次。不需要表示将整个列表保存在内存中,因此算法的渐近内存使用量正是表示布尔数组所需要的;即O(N) 位。

(相比之下,依赖于内存中排序或分区的算法假设您可以在内存中表示整个列表。在提出问题的形式中,这需要O(N) 64 位字。)


@Jorn cmets 认为第 1 步到第 3 步是计数排序的变体。从某种意义上说,他是对的,但差异很大:

  • 计数排序需要一个(至少)Xmax - Xmin 计数器数组,其中Xmax 是列表中的最大数字,Xmin 是列表中的最小数字。每个计数器必须能够代表 N 个状态;即假设一个二进制表示,它必须有一个整数类型(至少)ceiling(log2(N)) 位。
  • 要确定数组大小,计数排序需要对列表进行初始传递以确定XmaxXmin
  • 因此,最坏情况下的最小空间要求为ceiling(log2(N)) * (Xmax - Xmin) 位。

相比之下,上面介绍的算法在最坏和最好的情况下只需要N位。

但是,这种分析得出的直觉是,如果算法在列表中进行初始传递以寻找零(并在需要时计算列表元素),如果找到的话,它会给出一个更快的答案零。如果很有可能在列表中找到至少一个零,那么这样做绝对值得。而且这个额外的通道不会改变整体的复杂性。


编辑:我已将算法的描述更改为使用“布尔数组”,因为人们显然发现我使用位和位图的原始描述令人困惑。

【讨论】:

  • @adi92 如果步骤 3 为您提供了一个所有位都设置为 1 的位图,则该列表包含从 0 到 N-1 的每个值。这意味着列表中最小的非负整数是 N。如果 0 和 N-1 之间的任何值不在列表中,则不会设置相应的位。因此,最小的这样的值就是答案。
  • @adi92 在您的示例中,列表将包含 300 个元素。这意味着如果有任何“缺失”值,它必须小于 300。运行该算法,我们将创建一个具有 300 个插槽的位域,然后重复设置插槽 1、2 和 3 中的位,留下所有其他插槽 - 0 和 4 到 299 - 清除。当扫描位域时,我们会发现 slot 0 中的标志是清晰的,所以我们知道 0 就是答案。
  • 请注意,这个算法可能会更简单地理解,而无需旋转:“创建一个大小为 N 的布尔数组”等。一旦你理解了它,移动到按位版本在概念上很容易。
  • 在给出抽象解决方案时,使用概念上最简单的可行方法,不要过度专业化。您的解决方案要求使用(抽象)布尔数组,所以称之为。您可以通过bool[] 或位图来实现此数组,这与一般解决方案无关。
  • 我认为这个解决方案最好描述为“使用忽略 N 以上元素的计数排序,然后通过从头开始进行线性搜索来找到第一个缺失的元素。”
【解决方案3】:

由于 OP 现在已指定原始列表保存在 RAM 中,并且计算机只有 1GB 内存,所以我将冒险预测答案是否为零。

p>

1GB 的 RAM 意味着列表中最多可以包含 134,217,728 个数字。但是有 264 = 18,446,744,073,709,551,616 个可能的数字。因此,0 在列表中的概率是 137,438,953,472 中的 1。

相比之下,我成为struck by lightning this year 的几率是七十万分之一。而我getting hit by a meteorite 的几率约为 10 万亿分之一。因此,由于我被天体过早死亡,我被写在科学期刊上的可能性大约是答案不为零的十倍。

【讨论】:

  • 只有当值均匀分布并随机选择时,您的计算才成立。它们也可以按顺序生成。
  • 你是对的,当然。但我只想针对常见情况进行优化。 :)
  • 那么,被面试者被这个答案选中的几率是多少?
  • 问题并没有说这些数字是均匀随机选择的。它们由设置此问题的人选择。鉴于此,列表中 0 的概率大于 1 中的 137,438,953,472,甚至可能大于 2 中的 1。:-)
  • @Amarghosh 这个问题的答案也是零。
【解决方案4】:

正如在其他答案中指出的那样,您可以进行排序,然后简单地向上扫描,直到找到间隙。

您可以将算法复杂度提高到 O(N) 并保留 O(N) 空间,方法是使用修改后的 QuickSort 消除不是包含间隙的潜在候选者的分区。

  • 在第一个分区阶段,删除重复项。
  • 分区完成后,查看下分区中的项目数
  • 此值是否等于用于创建分区的值?
    • 如果是这样,则表明间隙位于更高的分区中。
      • 继续快速排序,忽略较低的分区
    • 否则间隙在下分区
      • 继续快速排序,忽略更高的分区

这样可以节省大量的计算量。

【讨论】:

  • 这很漂亮。它假设您可以在小于线性时间的时间内计算分区的长度,如果将其与分区数组一起存储,则可以完成。它还假设原始列表保存在 RAM 中。
  • 如果你知道列表的长度,你也可以剔除任何大于 len(list) 的值。根据鸽巢原则,任何“洞”都必须小于 len(list)。
  • 我不认为这是 O(n)... 首先,我不确定在列表完全排序之前是否可以删除重复项。其次,虽然您可以保证每次迭代都丢弃一半的搜索空间(因为您已划分为中点下方和上方),但您仍然对数据进行了多次传递(取决于 n)取决于 n。
  • paxdiablo:您可以使用 Stephen C 提出的位图方法构建一个只有唯一值的新列表。这在 O(n) 时间和空间中运行。我不确定它是否可以做得更好。
【解决方案5】:

由于数字都是 64 位长,我们可以在它们上使用radix sort,即 O(n)。对它们进行排序,然后扫描它们,直到找到你要找的东西。

如果最小的数字为零,则向前扫描,直到找到一个间隙。如果最小的数不为零,则答案为零。

【讨论】:

  • 是的,但是对于基数排序,内存要求可能会变得非常高。
  • 基数排序不适用于非常大的数据集。但是分区和基数排序可能会起作用。
【解决方案6】:

为了说明O(N) 思维的缺陷之一,这里有一个使用O(1) 空格的O(N) 算法。

for i in [0..2^64):
  if i not in list: return i

print "no 64-bit integers are missing"

【讨论】:

  • 威尔是对的。这不是 O(n) 因为你实际上有两个循环,但一个是隐式的。确定一个值是否在列表中是一个 O(n) 操作,并且您在 for 循环中执行了 n 次。这使得它 O(n^2)。
  • Nic,Will,它是 O(n * N),其中 n 是列表的大小,N 是域的大小(64 位整数)。虽然 N 是一个巨大的数字,但它仍然是一个常数,因此正式问题的复杂性是 O(n)。
  • 蚂蚁,我同意它是 O(nN),但 N 不是常数。因为算法在找到答案时结束,所以通过外循环的完整迭代次数等于答案,答案本身受列表大小的限制。因此,在这种情况下,O(Nn) 是 O(n^2)。
  • 在 N 个元素的列表中寻找一个数字显然是 O(N)。我们这样做了 2^64 次。虽然很大,但 2^64 是一个常数。所以算法是C*O(N),还是O(N)。
  • 我必须撤回我之前的声明;按照最严格的定义,这个操作确实是 O(n)。
【解决方案7】:

对于节省空间的方法并且所有值都是不同的,您可以在空间O( k ) 和时间O( k*log(N)*N ) 中执行此操作。它节省空间,没有数据移动,所有操作都是基本的(加减)。

  1. 设置U = N; L=0
  2. 首先对k 区域中的号码空间进行分区。像这样:
    • 0-&gt;(1/k)*(U-L) + L, 0-&gt;(2/k)*(U-L) + L, 0-&gt;(3/k)*(U-L) + L ... 0-&gt;(U-L) + L
  3. 找出每个地区有多少个号码 (count{i})。 (N*k 步数)
  4. 查找第一个未满的区域 (h)。这意味着count{h} &lt; upper_limit{h}。 (k 步数)
  5. 如果h - count{h-1} = 1 你得到了答案
  6. 设置U = count{h}; L = count{h-1}
  7. 转到 2

这可以使用散列来改进(感谢 Nic 这个想法)。

  1. 一样
  2. 首先对k 区域中的号码空间进行分区。像这样:
    • L + (i/k)-&gt;L + (i+1/k)*(U-L)
  3. inc count{j} 使用 j = (number - L)/k (if L &lt; number &lt; U)
  4. 找到第一个没有 k 个元素的区域 (h)
  5. 如果count{h} = 1h 是你的答案
  6. 设置U = maximum value in region hL = minimum value in region h

这将在O(log(N)*N) 中运行。

【讨论】:

  • 我真的很喜欢这个答案。读起来有点难,但和我读到这个问题时的想法很相似。
  • 在某些时候切换到 Stephen C 的位图解决方案会很聪明。可能在U-L &lt; k
  • 这不是在 O(log(N)*N) 中运行,而是在 O(N) 中运行。您的答案是@cdiggins 答案的概括,它在 O(N) 中运行,因为 sum(1/k**i for i in range(ceil(log_k(n))))
  • 每次迭代都会经过 O(N) 个数字,总共需要 O(log_k(N)) 次迭代。因此 O(log_k(N)*N) == O(log(N)*N)。原始数字未排序/存储,您需要遍历所有数字。
  • 但是,如果您将原始列表划分为 k 个区域(大小为 n/k),那么您选择第一个未满的区域。因此,在下一次迭代中,您只需要考虑所选区域并将其划分为 k 个新区域(大小为 n/k**2)等。实际上,您不会每次都迭代整个列表(否则分区的意义何在?)。
【解决方案8】:

我只是对它们进行排序,然后遍历序列,直到找到一个间隙(包括开头的零和第一个数字之间的间隙)。

就算法而言,这样的事情可以做到:

def smallest_not_in_list(list):
    sort(list)
    if list[0] != 0:
        return 0
    for i = 1 to list.last:
        if list[i] != list[i-1] + 1:
            return list[i-1] + 1
    if list[list.last] == 2^64 - 1:
        assert ("No gaps")
    return list[list.last] + 1

当然,如果您的内存比 CPU grunt 多得多,您可以创建一个包含所有可能的 64 位值的位掩码,并为列表中的每个数字设置位。然后在该位掩码中查找第一个 0 位。这在时间上变成了 O(n) 操作,但在内存需求方面非常昂贵:-)

我怀疑你是否可以改进 O(n),因为我看不到不涉及至少查看每个数字一次的方法。

该算法的算法如下:

def smallest_not_in_list(list):
    bitmask = mask_make(2^64) // might take a while :-)
    mask_clear_all (bitmask)
    for i = 1 to list.last:
        mask_set (bitmask, list[i])
    for i = 0 to 2^64 - 1:
        if mask_is_clear (bitmask, i):
            return i
    assert ("No gaps")

【讨论】:

  • 从描述来看,它似乎排除了 0 到第一个元素,因为它是不在列表中的最小元素。但是,这是我的假设,我可能是错的。
  • 我的想法是,如果排序的序列是 4,5,6,那么 0 将是最小的不在列表中。
  • 我预计 2、3、5,答案应该是 4,但是,我可能错了。
  • OP 应该回答的问题。搜索空间是“所有 64 位无符号整数”还是“列表中最低和最高之间的所有数字”?
  • 我同意在最坏的情况下您必须至少查看一次,除非它可能已经在二叉树中排序。
【解决方案9】:

对列表进行排序,查看第一个和第二个元素,然后开始向上直到有间隙。

【讨论】:

  • 看你怎么定义,不在列表中。
  • @PeterAllenWebb - 会有,但数字是随机顺序还是排序?
【解决方案10】:

你可以在 O(n) 的时间和 O(1) 的额外空间内完成它,尽管隐藏的因素非常大。这不是解决问题的实用方法,但它可能很有趣。

对于每个无符号 64 位整数(按升序)遍历列表,直到找到目标整数或到达列表末尾。如果到达列表末尾,则目标整数是不在列表中的最小整数。如果到达 64 位整数的末尾,则每个 64 位整数都在列表中。

这是一个 Python 函数:

def smallest_missing_uint64(source_list):
    the_answer = None

    target = 0L
    while target < 2L**64:

        target_found = False
        for item in source_list:
            if item == target:
                target_found = True

        if not target_found and the_answer is None:
            the_answer = target

        target += 1L

    return the_answer

这个函数故意保持 O(n) 效率低下。请特别注意,即使在找到答案之后,该函数也会继续检查目标整数。如果函数在找到答案后立即返回,则外部循环运行的次数将受答案大小的限制,答案的大小受 n 的限制。这种变化会使运行时间为 O(n^2),即使它会快很多。

【讨论】:

  • 是的。有趣的是,在这个问题上,一些 O(1) 空间和 O(n) 时间的算法在实践中失败了。
【解决方案11】:

感谢 egon、swilden 和 Stephen C 对我的启发。首先,我们知道目标值的范围,因为它不能大于列表的大小。此外,一个 1GB 的列表最多可以包含 134217728 (128 * 2^20) 个 64 位整数。

散列部分
我建议使用散列来显着减少我们的搜索空间。首先,对列表的大小进行平方根。对于 1GB 的列表,N=11,586。设置一个大小为 N 的整数数组。遍历列表,并将找到的每个数字的平方根 * 作为哈希值。在您的哈希表中,增加该哈希的计数器。接下来,遍历您的哈希表。您找到的第一个不等于其最大大小的存储桶定义了您的新搜索空间。

位图部分
现在设置一个与新搜索空间大小相等的常规位图,并再次遍历源列表,在找到搜索空间中的每个数字时填写位图。完成后,位图中的第一个未设置位将为您提供答案。

这将在 O(n) 时间和 O(sqrt(n)) 空间内完成。

(*您可以使用诸如位移之类的方法来更有效地执行此操作,并相应地改变存储桶的数量和大小。)

【讨论】:

  • 我喜欢将搜索空间划分为 Root-N 个桶以减少内存占用的想法,但列表中的重复项会破坏此方法。我想知道它是否可以修复。
  • 你说得对,我忽略了重复条目。我不确定这是否可以解决。
【解决方案12】:

如果一个数字列表中只有一个缺失的数字,找到缺失数字的最简单方法是将系列相加并减去列表中的每个值。最终值是缺失的数字。

【讨论】:

  • 是的。这是另一个经典的面试问题。
  • 比这更简单的是将列表中的数字异或,将范围内的数字异或,然后将结果异或。
【解决方案13】:
 int i = 0;
            while ( i < Array.Length)
            {

                if (Array[i] == i + 1)
                {
                    i++;
                }

                if (i < Array.Length)
                {
                    if (Array[i] <= Array.Length)
                    {//SWap

                        int temp = Array[i];
                        int AnoTemp = Array[temp - 1];
                        Array[temp - 1] = temp;
                        Array[i] = AnoTemp;

                    }
                    else
                       i++;



                }
            }

            for (int j = 0; j < Array.Length; j++)
            {
                if (Array[j] > Array.Length)
                {
                    Console.WriteLine(j + 1);
                    j = Array.Length;
                }
                else
                    if (j == Array.Length - 1)
                        Console.WriteLine("Not Found !!");

            }
        }

【讨论】:

    【解决方案14】:

    我们可以使用哈希表来保存数字。完成所有数字后,从 0 开始运行计数器,直到找到最小值。一个相当好的散列将在恒定时间内散列和存储,并在恒定时间内检索。

    for every i in X         // One scan Θ(1)
       hashtable.put(i, i);  // O(1)
    
    low = 0;
    
    while (hashtable.get(i) <> null)   // at most n+1 times
       low++;
    
    print low;
    

    最坏的情况是数组中有n元素,并且是{0, 1, ... n-1},这种情况下会在n得到答案,仍然保持O(n)

    【讨论】:

      【解决方案15】:

      这是我用 Java 编写的答案:

      基本理念: 1- 遍历数组,丢弃重复的正数、零和负数,同时对其余数求和,同时获得最大正数,并将唯一的正数保留在 Map 中。

      2- 将总和计算为 max * (max+1)/2。

      3- 求第 1 步和第 2 步计算的总和之间的差

      4- 再次从 1 循环到 [sums difference, max] 的最小值,并返回第一个不在步骤 1 中填充的映射中的数字。

      public static int solution(int[] A) {
          if (A == null || A.length == 0) {
              throw new IllegalArgumentException();
          }
      
          int sum = 0;
          Map<Integer, Boolean> uniqueNumbers = new HashMap<Integer, Boolean>();
          int max = A[0];
          for (int i = 0; i < A.length; i++) {
              if(A[i] < 0) {
                  continue;
              }
              if(uniqueNumbers.get(A[i]) != null) {
                  continue;
              }
              if (A[i] > max) {
                  max = A[i];
              }
              uniqueNumbers.put(A[i], true);
              sum += A[i];
          }
          int completeSum = (max * (max + 1)) /  2;
          for(int j = 1; j <= Math.min((completeSum - sum), max); j++) {
              if(uniqueNumbers.get(j) == null) { //O(1)
                  return j;
              }
          }
          //All negative case
          if(uniqueNumbers.isEmpty()) {
              return 1;
          }
          return 0;
      }
      

      【讨论】:

        【解决方案16】:

        正如 Stephen C 巧妙地指出的那样,答案必须是一个小于数组长度的数字。然后我会通过二进制搜索找到答案。这优化了最坏的情况(因此面试官无法在“假设”的病态场景中抓住你)。在采访中,请指出您这样做是为了针对最坏的情况进行优化。

        使用二分搜索的方法是从数组的每个元素中减去您要查找的数字,并检查是否有否定结果。

        【讨论】:

          【解决方案17】:

          我喜欢“猜测零”的方法。如果数字是随机的,则很可能为零。如果“考官”设置了一个非随机列表,那就加一个再猜:

          LowNum=0
          i=0
          do forever {
            if i == N then leave /* Processed entire array */
            if array[i] == LowNum {
               LowNum++
               i=0
               }
             else {
               i++
             }
          }
          display LowNum
          

          最坏的情况是 n*N 且 n=N,但实际上 n 很可能是一个很小的数字(例如 1)

          【讨论】:

            【解决方案18】:

            我不确定我是否收到了这个问题。但是如果对于列表 1,2,3,5,6 并且缺少的数字是 4,那么可以通过以下方式在 O(n) 中找到缺少的数字: (n+2)(n+1)/2-(n+1)n/2

            编辑:对不起,我想我昨晚想得太快了。无论如何,第二部分实际上应该由 sum(list) 代替,这就是 O(n) 的来源。该公式揭示了其背后的思想:对于 n 个连续整数,总和应为 (n+1)*n/2。如果有缺失的数字,总和将等于 (n+1) 个连续整数的总和减去缺失的数字。

            感谢您指出我正在考虑一些中间部分的事实。

            【讨论】:

            • 我不知道,乍一看,这是如何工作的。在您的情况下,n=5 并且公式将被修复,无论它缺少什么数字。
            • Simon:您现在可以根据我的编辑删除反对票吗?
            【解决方案19】:

            干得好蚂蚁阿斯玛!我思考了大约 15 分钟的答案,并独立想出了一个与您的想法相似的答案:

            #define SWAP(x,y) { numerictype_t tmp = x; x = y; y = tmp; }
            int minNonNegativeNotInArr (numerictype_t * a, size_t n) {
                int m = n;
                for (int i = 0; i < m;) {
                    if (a[i] >= m || a[i] < i || a[i] == a[a[i]]) {
                        m--;
                        SWAP (a[i], a[m]);
                        continue;
                    }
                    if (a[i] > i) {
                        SWAP (a[i], a[a[i]]);
                        continue;
                    }
                    i++;
                }
                return m;
            }
            

            m 表示“考虑到我对第一个 i 输入的了解,并且在输入 m-1 之前对这些值没有任何其他假设,当前可能的最大输出”。

            仅当 (a[i], ..., a[m-1]) 是值 (i, ..., m-1) 的排列时,才会返回 m 的值。因此,如果 a[i] >= m 或 a[i]

            如果这不是真的但是 a[i] > i 那么知道 a[i] != a[a[i]] 我们知道用 a[a[i]] 交换 a[i] 会增加各自位置的元素数量。

            否则 a[i] 必须等于 i 在这种情况下,我们可以增加 i ,因为知道直到并包括该索引的所有值都等于它们的索引。

            这不能进入无限循环的证明留给读者作为练习。 :)

            【讨论】:

              【解决方案20】:

              来自 Ants 答案的 Dafny 片段显示了原地算法可能失败的原因。 requires 前置条件描述了每一项的值不能超出数组的范围。

              method AntsAasma(A: array<int>) returns (M: int)
                requires A != null && forall N :: 0 <= N < A.Length ==> 0 <= A[N] < A.Length;
                modifies A; 
              {
                // Pass 1, move every value to the position of its value
                var N := A.Length;
                var cursor := 0;
                while (cursor < N)
                {
                  var target := A[cursor];
                  while (0 <= target < N && target != A[target])
                  {
                      var new_target := A[target];
                      A[target] := target;
                      target := new_target;
                  }
                  cursor := cursor + 1;
                }
              
                // Pass 2, find first location where the index doesn't match the value
                cursor := 0;
                while (cursor < N)
                {
                  if (A[cursor] != cursor)
                  {
                    return cursor;
                  }
                  cursor := cursor + 1;
                }
                return N;
              }
              

              将代码粘贴到带有和不带有forall ... 子句的验证器中以查看验证错误。第二个错误是验证者无法为 Pass 1 循环建立终止条件的结果。证明这一点留给更了解该工具的人。

              【讨论】:

                【解决方案21】:

                这是 Java 中的一个答案,它不修改输入并使用 O(N) 时间和 N 位加上一个小的恒定内存开销(其中 N 是列表的大小):

                int smallestMissingValue(List<Integer> values) {
                    BitSet bitset = new BitSet(values.size() + 1);
                    for (int i : values) {
                        if (i >= 0 && i <= values.size()) {
                            bitset.set(i);
                        }
                    }
                    return bitset.nextClearBit(0);
                }
                

                【讨论】:

                  【解决方案22】:
                  def solution(A):
                  
                  index = 0
                  target = []
                  A = [x for x in A if x >=0]
                  
                  if len(A) ==0:
                      return 1
                  
                  maxi = max(A)
                  if maxi <= len(A):
                      maxi = len(A)
                  
                  target = ['X' for x in range(maxi+1)]
                  for number in A:
                      target[number]= number
                  
                  count = 1
                  while count < maxi+1:
                      if target[count] == 'X':
                          return count
                      count +=1
                  return target[count-1] + 1
                  

                  上述解决方案获得 100%。

                  【讨论】:

                    【解决方案23】:

                    1)过滤负数和归零

                    2)排序/区分

                    3)访问数组

                    复杂度:O(N) 或 O(N * log(N))

                    使用 Java8

                    public int solution(int[] A) {
                                int result = 1;
                        boolean found = false;
                        A = Arrays.stream(A).filter(x -> x > 0).sorted().distinct().toArray();
                        //System.out.println(Arrays.toString(A));
                        for (int i = 0; i < A.length; i++) {
                            result = i + 1;
                            if (result != A[i]) {
                                found = true;
                                break;
                            }
                        }
                        if (!found && result == A.length) {
                            //result is larger than max element in array
                            result++;
                        }
                        return result;
                    }
                    

                    【讨论】:

                      【解决方案24】:

                      一个unordered_set可以用来存储所有的正数,然后我们可以从1迭代到unordered_set的长度,看看第一个没有出现的数。

                      int firstMissingPositive(vector<int>& nums) {
                      
                          unordered_set<int> fre;
                          // storing each positive number in a hash.
                          for(int i = 0; i < nums.size(); i +=1)
                          {
                              if(nums[i] > 0)
                                  fre.insert(nums[i]);
                           }
                      
                          int i = 1;
                          // Iterating from 1 to size of the set and checking 
                          // for the occurrence of 'i'
                      
                          for(auto it = fre.begin(); it != fre.end(); ++it)
                          {
                              if(fre.find(i) == fre.end())
                                  return i;
                              i +=1;
                          }
                      
                          return i;
                      }
                      

                      【讨论】:

                        【解决方案25】:

                        通过基本的javascript解决

                        var a = [1, 3, 6, 4, 1, 2];
                        
                        function findSmallest(a) {
                        var m = 0;
                          for(i=1;i<=a.length;i++) {
                            j=0;m=1;
                            while(j < a.length) {
                              if(i === a[j]) {
                                m++;
                              }
                              j++;
                            }
                            if(m === 1) {
                              return i;
                            }
                          }
                        }
                        
                        console.log(findSmallest(a))

                        希望这对某人有所帮助。

                        【讨论】:

                          【解决方案26】:

                          用python它不是最有效的,但是正确的

                          #!/usr/bin/env python3
                          # -*- coding: UTF-8 -*-
                          import datetime
                          
                          # write your code in Python 3.6
                          
                          def solution(A):
                              MIN = 0
                              MAX = 1000000
                              possible_results = range(MIN, MAX)
                          
                              for i in possible_results:
                                  next_value = (i + 1)
                                  if next_value not in A:
                                      return next_value
                              return 1
                          
                          test_case_0 = [2, 2, 2]
                          test_case_1 = [1, 3, 44, 55, 6, 0, 3, 8]
                          test_case_2 = [-1, -22]
                          test_case_3 = [x for x in range(-10000, 10000)]
                          test_case_4 = [x for x in range(0, 100)] + [x for x in range(102, 200)]
                          test_case_5 = [4, 5, 6]
                          print("---")
                          a = datetime.datetime.now()
                          print(solution(test_case_0))
                          print(solution(test_case_1))
                          print(solution(test_case_2))
                          print(solution(test_case_3))
                          print(solution(test_case_4))
                          print(solution(test_case_5))
                          

                          【讨论】:

                            【解决方案27】:
                            def solution(A):
                                A.sort()
                                j = 1
                                for i, elem in enumerate(A):
                                    if j < elem:
                                        break
                                    elif j == elem:
                                        j += 1
                                        continue
                                    else:
                                        continue
                                return j
                            

                            【讨论】:

                              【解决方案28】:

                              这会有所帮助:

                              0- A is [5, 3, 2, 7];
                              1- Define B With Length = A.Length;                            (O(1))
                              2- initialize B Cells With 1;                                  (O(n))
                              3- For Each Item In A:
                                      if (B.Length <= item) then B[Item] = -1                (O(n))
                              4- The answer is smallest index in B such that B[index] != -1  (O(n))
                              

                              【讨论】:

                              猜你喜欢
                              • 2010-10-22
                              • 1970-01-01
                              • 1970-01-01
                              • 2020-01-29
                              • 1970-01-01
                              • 2013-06-27
                              • 1970-01-01
                              • 2014-01-02
                              • 2021-10-07
                              相关资源
                              最近更新 更多