在线性时间和常量空间内,找到一个整数数组中第一个缺失的正整数。

22

换句话说,找到数组中不存在的最小正整数。数组可以包含重复项和负数。 Stripe在其编程面试中提出了这个问题。我已经为此设计了以下解决方案:

#include<bits/stdc++.h>
using namespace std;

int main(){
    int arr[]={1,-1,-5,-3,3,4,2,8};
    int size= sizeof(arr)/sizeof(arr[0]);
    sort(arr, arr+size);
    int min=1;

    for(int i=0; i<size; i++){
        if(arr[i]>min) break;
        if(arr[i]==min) min=min+1;
    }
    cout<<min;
    return 0;
}

这里,我首先对数组进行排序,然后遍历一次数组。在遍历数组之前,我初始化了一个名为“min”的变量,其值为1。现在,在遍历数组时,当我们获取一个等于min的整数时,我们只需将min的值增加即可。这确保了min变量保存最新的尚未出现的最小正整数。 你能想到更好的方法吗?提前感谢您。


1
@DillonDavis 实际上,如果数字范围是有限的,你是可以的。 - Henry
1
@Henry,我刚刚谷歌了一下——计数排序确实有一个原地变体。我承认我错了。 - Dillon Davis
1
实际上,如果数字是“特殊的”(即整数),你是可以的。https://dev59.com/ZnE95IYBdhLWcg3wV8e_ - Z .
2
你应该明确指定数组是可修改的。 - user202729
可能是[缺失整数变体-O(n)解决方案需要]的重复问题。 - Richardissimo
显示剩余13条评论
16个回答

35
假设可以修改数组, 1. 我们将数组分成两部分,其中第一部分仅包含正数。假设我们的起始索引为0,终止索引为end(不含)。 2. 我们从索引0到end遍历数组。我们获取该索引处元素的绝对值 - 假设值为x。 1. 如果x>end,我们什么也不做。 2. 如果不是,则将索引x-1处的元素的符号变为负数。(澄清:我们不切换符号。 如果值为正,则变为负数。 如果它是负数,则仍为负数。在伪代码中,这将类似于if(arr [x-1]> 0)arr [x-1] = -arr [x-1],而不是arr [x-1] = -arr [x-1]。) 3. 最后,我们再次从索引0到end遍历数组。如果我们在某个索引处遇到正元素,则输出索引+1。 这就是答案。 但是,如果我们没有遇到任何正元素,则意味着整数1到end出现在数组中。我们输出end + 1。 所有步骤可以在O(n)时间内完成,并且使用O(1)空间。 示例:
Initial Array:            1 -1 -5 -3 3 4 2 8
Step 1 partition:         1 8 2 4 3 | -3 -5 -1, end = 5

在步骤2中,我们改变正数的符号以跟踪已经出现的整数。例如,在这里,array [2] = -2 <0 ,它表明 2 + 1 = 3 已经在数组中出现过了。基本上,如果 i + 1 在数组中,我们会将索引为 i 的元素的值更改为负数。

Step 2 Array changes to: -1 -8 -2 -4 3 | -3 -5 -1
在第三步中,如果某个值array[index]为正数,则意味着我们在第二步中未找到任何值为index + 1的整数。
Step 3: Traversing from index 0 to end, we find array[4] = 3 > 0
        The answer is 4 + 1 = 5

1
以上解决方案未处理的一种情况是数组中全为非正整数的情况。在这种情况下,答案应该是1。 - Jhanak Didwania
@pmcarpan,由于第2.2步骤,一些带有双精度的数组无法正常工作。 数组[7,1,2,2]变成了[-7,1,2,2]--> [-7,-1,2,2] --> [-7,1,2,2]。 然后由于第3步骤,它返回2。 我误解了什么? - Bn.F76
当我们将符号变为负数时,我们不会切换它。在你提到的例子中,[7,1,2,2] -> [-7,1,2,2] -> [-7,-1,2,2] -> [-7,-1(原本是负数,仍然是负数),2,2] - pmcarpan
1
@yosemite_k 因为它可能已经被切换并且为负数。例如,如果您有输入数组 [2,3],第一步将找到 2 并切换索引为 2-1=1 的数字,这将导致数组变为 [2,-3]。然而,在下一步中,您希望取一个 3 而不是 -3,因此需要使用绝对值。 - Luis Aceituno
1
@Kaushal28 在第2.2步中,我们不会切换。一旦符号为负,它将保持为负。对于[1,1],第1个1变为负数后,第0个索引仍然为负数。"使元素的符号变为负数"意味着符号保持为负数,并且考虑了两种情况:1)当它为正数时;2)当它已经为负数(在这种情况下,使符号为负数没有任何效果,因为元素已经为负数)。我确实理解这种措辞会导致一些误解,因此我会添加一个澄清。 - pmcarpan
显示剩余9条评论

5

这实际上是一道LeetCode问题,要求时间复杂度为O(n),空间复杂度为O(1),因此对输入进行排序或转换成集合都不适用。以下是Python 3的实现,能够在O(n)的时间复杂度下使用O(1)的空间复杂度。基本思路是把每个元素i放到nums[i-1]中。

def missing_int(self, nums: List[int]) -> int:
    i = 0
    n = len(nums)

    while i < n:
        j = nums[i]
        if 0 < j < n and j != nums[j - 1]:
            nums[i], nums[j - 1] = nums[j - 1], nums[i]
        else:
            i += 1

    return next((x + 1 for x in range(n) if nums[x] != x + 1), n + 1)

测试:

def test_missing_int(self):
    assert func.missing_int([1, 2, 1, 0]) == 3
    assert func.missing_int([3, 4, -1, 1]) == 2
    assert func.missing_int([7, 8, 9, 11, 12]) == 1
    assert func.missing_int([1]) == 2
    assert func.missing_int([]) == 1
    assert func.missing_int([0]) == 1
    assert func.missing_int([2, 1]) == 3
    assert func.missing_int([-1, -2, -3]) == 1
    assert func.missing_int([1, 1]) == 2
    assert func.missing_int([1000, -1]) == 1
    assert func.missing_int([-10, -3, -100, -1000, -239, 1]) == 2
    assert func.missing_int([1, 1]) == 2

还要进行测试。 - Surt
1
“没有测试的代码是糟糕的代码。无论它写得多么好,无论它有多漂亮、面向对象或封装良好,如果没有测试,我们就无法快速和可靠地改变代码的行为。没有测试,我们真的不知道我们的代码是变得更好还是更糟。”
  • 迈克尔·菲瑟斯,《遗留代码有效工作》。
- Abhijit Sarkar
Q1: swap是Python3的函数吗?如果不是,nums[i],nums[hi] = nums[hi],nums[ii]也可以工作。 Q2:为什么需要elif nums [i]> pivot? - Albe
@Albe swap 不是 Python 内置函数,我忘了展示它的实现。至于 elif,我们在这里做的是将输入数组分成正数和负数两部分。如果还不清楚,我建议您使用给定的测试和调试器来逐步执行代码。 - Abhijit Sarkar
@AbhijitSarkar 非常感谢您的建议。我刚刚尝试了一下,我怀疑 elif nums[i] > pivot: 这行代码是不必要的。因为我们从左到右进行交换,如果 nums[i] > pivot,那么代码将当前数字(它是正数)与左边的另一个正数进行交换(因为 lo <= ii)。删除 elif 和随后的三行代码不会改变最终结果。 - Albe
1
@Albe 确实,我已经用更简单的实现更新了代码。 - Abhijit Sarkar

2

PMCarpan的算法是有效的。

我认为你的方法是可行的,但是你应该指定排序类型,以便清楚地表明它是一个线性排序,而不一定是整个数组的完整排序。 这样可以在不使用任何空间的情况下达到O(N)时间。

扫描数组,如果当前索引中的值小于数组的长度,则将其与当前索引中的值交换。您必须继续交换,直到在每个索引处都不再有意义为止。然后最后再进行一次扫描,直到找到一个不正确的索引为止。

这里是一些工作的Python代码,尽管Python不是做这种排序的好地方,哈哈。

def sortOfSort(arr) :
    for index in range(len(arr)) :
        checkValue = arr[index]

        while(checkValue > 0 and checkValue != index and checkValue < len(arr) and arr[checkValue] != checkValue) :
            arr[index] = arr[checkValue]
            arr[checkValue] = checkValue
            checkValue = arr[index]

    return arr[1:] + [arr[0]]

def findFirstMissingNumber(arr) :
    for x in range(len(arr)) :
        if (x+1 != arr[x]) :
            return x+1
    return len(arr) + 1

return arr[1:]这部分的原因是,根据您的描述,我们不将零作为起始点。


你已经测试过 [3, 2, 1, 6, 5, 4] 了吗? - yosemite_k
@yosemite_k 由于这个问题是在2018年提出的,我不知道当时是否测试过它,甚至是否进行了任何测试,但我现在刚刚测试了一下,它产生了“7”,我相信这是正确的答案。 - FredMan
对我来说,它产生了“1”。 - yosemite_k
1
有两个函数,你必须将第一个函数的输出作为第二个函数的输入。例如:findFirstMissingNumber(sortOfSort([3, 2, 1, 6, 5, 4]))。 - FredMan
我明白了,那么最好修改代码,在 findFirstMissing 函数内部调用 sortOfSort - yosemite_k

2
许多答案概述了算法或提供了解决问题的代码,这非常有帮助。令人惊奇的是,许多顶级答案都是基于同一种算法洞察力的变化(本质上)。实际上,通过走过特定的推理路径,我们可以重新发明它们,以一种更能让我们理解为什么它们正确工作的方式。
让我们从一些简单的观察开始。首先,如果我们有一个包含n个项目的数组,则最小的缺失整数必须是1、2、3、...、n或n+1中的一个。原因是,如果任何1、2、3、...或n中的任何一个值不在数组中,则在该范围内缺少的最小值必须是我们想要的值。否则,如果所有值1、2、3、...和n都在数组中,则最小缺失值为n+1。
这个观点对我们很有帮助,因为它让我们重新定义了我们要寻找的内容。具体而言,我们的任务将是追踪原始数组中存在哪些数字1、2、3、...和n。如果我们能做到这一点,解决问题就相对简单了。一旦我们知道哪些项目存在,我们就可以从1、2、3、...、n开始计数,直到找到第一个缺失的数字。如果这些值中没有任何一个缺失,那么答案就是n+1。
现在,我们的目标是找出如何在使用少量时间和空间的情况下完成此操作。
让我们先采用一种比允许的更多时间和空间的方法,然后看看如何将其优化到O(n)时间和O(1)辅助空间。我们将维护一个辅助集合,以便我们可以轻松地看到数组中存在什么。然后,我们将遍历数组,并为每个值添加到集合中,对于介于1和n之间的每个值,包括1和n。然后,我们将向上计数,以查看缺失的值是哪个。以下是一些代码:
# Initial implementation; doesn't meet the time or
# space bounds.
def first_missing_integer(arr):
    # Track which items are used
    used = set()
    
    # Add each item from the array if it's in range
    for i in arr:
        if 1 <= i <= len(arr):
            used.add(i)
    
    # Count upward as 1, 2, 3, ..., n to see if any of
    # those are the values we want.
    for i in range(1, len(arr)):
        if i not in used:
            return i
    
    # If not, the answer is n+1.
    return len(arr)

这个算法的速度有多快,需要占用多少空间呢?维护一个辅助集合需要 O(n) 的辅助存储空间。我们循环遍历所有 n 个项目并将它们添加到一个 set 中,这需要预计时间 O(n)。然后,我们从 1 到 n 计数,查询我们的集合,这需要预计时间 O(n)。总体而言,这意味着我们的算法使用预计的 O(n) 时间和 O(n) 的辅助存储空间。我们在两个方面都未达到目标:
  • 我们希望使用 O(1) 的辅助空间,而不是 O(n) 的辅助空间。
  • 我们希望运行时间为最坏情况下的 O(n),而不是期望的 O(n)。

为了解决这些问题,让我们减少空间使用率。现在,我们正在使用一个辅助集合。但是,我们知道集合中的所有值都将是 1 到 n 之间(包括 1 和 n)的值。那么这可能会引导我们问:是否有更好的数据结构可以用来存储这个集合?

由于我们知道将存储的值的确切范围,因此在这里一个合理的选择是从哈希表切换到位向量。我们将创建一个n位的数组,初始值全部为0。每当我们看到一个适当范围内的值时,我们就会将相应的位翻转为1。这意味着我们不再需要使用哈希,空间开销降到了n个位的内存,而不是n个字的内存。它仍然是O(n)的空间复杂度,但具有更小的前导系数。很棒!
以下是此实现的示例:
# Improved implementation using a bitvector. This still
# doesn't meet the space bounds, but does meet the time
# requirements.
def first_missing_integer(arr):
    # n-element bitvector to track which items are
    # present. False means the item is not here, and
    # True means the item is here.
    used = [False] * len(arr)
    
    # Mark each item from the array if it's in range
    for i in arr:
        if 1 <= i <= len(arr):
            # The values range from 1 to n, but our
            # array indices run from 0 to n-1, so we
            # need to subtract 1 from the value.
            used[i - 1] = True
    
    # Scan the bits to find the first one that wasn't
    # set.
    for i in range(len(arr)):
        if not used[i]:
             # As above, our indices are shifted here
             # in that index i corresponds to the value
             # i + 1.
             return i + 1
    
    # If not, the answer is n+1.
    return len(arr)

这是我们先前方法的改进。现在我们以最坏情况下 O(n) 的时间运行,因为不需要哈希或随机性。而且我们的空间使用率已经降低:我们现在使用 n 个辅助位。但仍然需要 O(n) 的辅助内存。这还不够好。我们能解决这个问题吗?
关键在于我们只允许使用 O(1) 的辅助存储空间,而不是 O(1) 的总空间。也就是说,我们可以随意修改原始数组,只要我们在该数组之上只使用 O(1) 的内存即可。这打开了一个有趣的可能性:我们能否将数组本身用作位向量?也就是说,我们是否可以在原始数组中创造性地修改,来隐式表示此位向量,而不是分配一个次要位向量来跟踪存在的内容?
幸运的是,答案是肯定的。而且不仅如此,“是”还有很多不同的方法。实际上,这个问题的大多数顶级答案本质上都是“在数组本身内部编码位向量”的变体。
为了使这个想法起作用,我们需要一些方式来“标记”数组中的位置。具体而言,如果数字x + 1在某处存在,我们希望“标记”数组中的位置x。我们可以使用什么来标记该位置呢?那么,使用数字本身如何?我们可以随意重新排列数组中的项目。让我们使用以下系统:如果值x在数组中,则应将其放在位置x-1。然后,我们可以通过查看arr[x - 1] == x来检查x是否存在。这种方法使用每个元素的位置隐式编码了我们的位向量。
事实证明,有一种好的方法可以做到这一点。看一下数组的第一个元素。如果该元素为0、负数或大于n + 1,则我们不需要对其进行任何操作,因为它对我们没有帮助。否则,它在1到n之间,并且需要在数组中某个位置。具体而言,如果我们正在查看的值是x,则我们希望将x放在位置x-1。这可以有两种选择。
如果位置 x - 1 上的项已经等于 x,则无需再做任何操作以将 x 放置在位置 x - 1 上。 否则,在位置 x - 1 上有其他某个项。将该项与 x 交换。然后,需要将该项放置在其他位置,因此重复此过程。 如果我们从左到右扫描数组,并根据需要移动和移动项目,那么最多只会移动每个项目一次。为什么?因为只有当一个项目被交换到它应该在的位置时,或者不在应该在的位置并被替换为另一个元素时,才会移动该项目。这意味着交换共计花费 O(n) 的时间,仅需要 O(1) 的辅助空间。(具有现代代数背景的人可能会认识到这本质上是计算部分排列元素 1、2、...、n 的循环分解并将其反转。)
一旦我们交换了所有的东西,我们就可以使用之前的策略,从1、2、3、...一直数到n,以查看缺少哪个项目。我们不再检查位向量,而是检查原始数组中每个位置上的内容。具体如下:
# Runs in O(n) time and uses O(1) auxiliary space, but
# rearranges the elements of the array
def first_missing_integer(arr):
    # Move each item x where 1 <= x <= n to position
    # x - 1. We sweep from left to right, moving items
    # as needed.
    for i in range(len(arr)):
         # Which item is here?
         toPlace = arr[i]
         
         # This item needs to swap with index toPlace - 1
         # unless (1) the item isn't in the range from
         # 1 to n, (2) the item at index toPlace - 1 is
         # already at the proper index.
         while 1 <= toPlace <= len(arr) and arr[toPlace - 1] != toPlace:
             arr[i], arr[toPlace - 1] = arr[toPlace - 1], arr[i]
             toPlace = arr[i]
    
    # At this point, the array is structured so that if
    # x appears in the array for some 1 <= x <= n,
    # then arr[x - 1] == x. To find the missing integer,
    # we count up from 1, 2, 3, ..., n to see which
    # is the first missing value.
    for i in range(len(arr)):
        if arr[i] != i + 1:
             return i + 1
    
    # All values from 1 to n are present, so n+1 is 
    # the smallest missing one.
    return len(arr)

这种方法与Abhijit Sarkar提出的方法、Anubis提出的方法以及sarkar提出的方法密切相关。由于它仅仅是对数字进行重新排列,不改变这些值的内容,因此它的优点是无论原始数字采用何种格式(例如:有符号32位整数、无符号64位整数等),都可以使用该方法。
另一方面,假设我们使用的是带符号整数(比如Java的int类型)。我们知道我们只关心1、2、3、...、n这些正数。因此,这些数字的符号位始终为0。因此,我们可以考虑“窃取”该位,并在输入数组中直接编码我们原始的位向量思想。例如,假设我们有以下输入数组:
Array:  3,  1,  4,  1,  5,  9,  2
Index:  0   1   2   3   4   5   6

我们可以扫描输入数组。当我们读取一个值x,其中1 ≤ x ≤ n时,我们将在位置x-1处的元素设置为负数,通过设置其符号位。在这种情况下,当我们完成时,它看起来像这样:
Array: -3, -1, -4, -1, -5,  9,  2
Index:  0   1   2   3   4   5   6

现在,我们只需要找到第一个非负值。该值的索引对应于位向量中未被设置的第一个位(这里是索引5),因此我们的答案将是5加1再得到6。
这里的一个复杂性在于,我们的输入数组可以包含负数,并且那些已经存在的负数可能会通过虚假地指示缺失数字而混淆这种方法。同样,数字0也会搞砸它,因为0=-0。解决这个问题的一种方法是重新排列数组中的数字,以便所有非正数(负数和零)值存储在数组的末尾,正数值放在前面。从那里开始,我们只需假装我们正在处理由正值形成的子数组即可。(这本质上就是@pmcarpan和Harlan Gray提出的方法。)编写代码需要使用原地分区算法来移动事物。幸运的是,C++有这样的算法作为其标准库的一部分,这意味着我们可以像这样编写代码:
int32_t first_missing_integer(std::vector<int32_t>& arr) {
    /* Reorder the elements to put the positive elements at
     * the front of the vector and the remaining elements
     * at the back.
     */
    auto boundary = std::partition(arr.begin(), arr.end(),
                                   [](int32_t val) {
                                       return val > 0;
                                   });

    /* The boundary position gives us the new "effective" length
     * of the input array.
     */
    size_t n = boundary - arr.begin();

    /* Scan across the first n elements, using the sign bit
     * of the appropriate array slots to store which items
     * are present. Since we're making elements negative
     * as we go, we need to work with the absolute values
     * of the relevant elements. And fortunately, that will
     * never cause an integer overflow, since we know all
     * these values were positive to begin with.
     */
    for (int32_t elem: arr) {
        int32_t value = std::abs(elem);

        /* The value must be between 1 and n for us to care
         * about it, and the destination number needs to
         * be positive for us to flip it.
         */
        if (1 <= value && value <= n && arr[value] > 0) {
            arr[value] *= -1;
        }
    }

    /* Now, count from 1 to n to find the first missing value,
     * which will be indicated by the first positive
     * number.
     */
    for (int32_t i = 1; i <= n; i++) {
        if (arr[i - 1] > 0) {
             return i;
        }
    }

    /* Looks like all of them were present. The first missing
     * integer is n + 1.
     */
    return n + 1;
}

希望这有所帮助!

哇!这就像是一篇小论文。讲解得非常清楚,应该成为新的被接受的答案。赞! - Metro Smurf

2

这很简单。(解决方案不是我提出的)

public static int Missing(int[] a)
{
    // the idea is to put all values in array on their ordered place if possible
    for (int i = 0; i < a.Length; i++)
    {
        CheckArrayAtPosition(a, i);
    }

    for (int i = 0; i < a.Length; i++)
        if (a[i] != i + 1)
            return i + 1;
    return a.Length + 1;
}

private static void CheckArrayAtPosition(int[] a, int i)
{
    var currentValue = a[i];
    if (currentValue < 1) return; // do not touch negative values because array indexes are non-negative
    if (currentValue > a.Length) return; // do not touch values that are bigger than array length because we will not locate them anyway
    if (a[currentValue - 1] == currentValue) return; // do not need to change anything because index contain correct value already
    Swap(a, i, currentValue - 1);
    CheckArrayAtPosition(a, i); // now current position value is updated so we need to check current position again
}

private static void Swap(int[] a, int i, int j)
{
    int temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}

这里有一个递归,但由于每次交换都会将1个值放到正确的位置,所以最多只需要进行n次交换。时间复杂度为线性。

需要注意的是,本文中的"Swap"指的是一种排序算法中的操作,而非普通意义上的交换。


0

这是我在LeetCode上被接受的答案

    def firstMissingPositive(self, nums):
        if 1 not in nums:
            return 1
        n = len(nums)
        for i in range(n):
            if nums[i] > n or nums[i] <= 0:
                nums[i] = 1
        for i in range(n):
            a = abs(nums[i])
            if a == n:
                nums[0] = -abs(nums[0])
            else:
                nums[a] = -abs(nums[a])
        for i in range(2, n):
            if nums[i] > 0: return i
        return n if nums[0] > 0 else n+1

0

PMCarpan算法的Javascript实现

function getMissingInt(array) {
    array = array.filter((a) => a > 0);
    for (let i = 0; i < array.length; i++) {
        const value = Math.abs(array[i]);
        if (value <= array.length && array[value - 1] > 0) {
            array[value - 1] *= -1;
        }
    }
    for (let i = 0; i < array.length; i++) {
        if (array[i] > 0) {
            return i + 1;
        }
    }
    return array.length + 1;
}
console.log(getMissingInt([1, -1, -5, -3, 3, 4, 2, 8]));
console.log(getMissingInt([3, 4, -1, 1]));
console.log(getMissingInt([1, 2, 0]));

具有恒定的空间和时间:

function segregateItems(array) {
    let end = array.length - 1;
    for (let i = 0; i < end+1; i++) {
        if (array[i] <= 0) {
            [array[i], array[end]] = [array[end], array[i]];
            end--;
        }
    }
    return end + 1;
}
function getMissingInt(array) {
    let positiveListLength = segregateItems(array);
    for (let i = 0; i < positiveListLength; i++) {
        const value = Math.abs(array[i]);
        if (value <= positiveListLength && array[value - 1] > 0) {
            array[value - 1] *= -1;
        }
    }
    for (let i = 0; i < positiveListLength; i++) {
        if (array[i] > 0) {
            return i + 1;
        }
    }
    return positiveListLength + 1;
}
console.log(getMissingInt([1, -1, -5, -3, 3, 4, 2, 8]));
console.log(getMissingInt([3, 4, -1, 1]));
console.log(getMissingInt([1, 2, 0]));


1
array.filter 函数返回一个新的值数组,需要 O(n) 的辅助存储空间。因此,它不使用 O(1) 的辅助空间。 - templatetypedef
1
请纠正我,但splice不是需要将删除后的所有元素向下移动一个位置,因此需要时间O(n)吗?如果是这样,那么这将在时间O(n^2)内运行。 - templatetypedef
@templatetypedef 是的,同意了。除了旧的过滤器方法,还添加了更新的代码片段。 - Shub

0

这是一个C语言实现
输入

#include <stdio.h>
#include <stdlib.h>
//Here we separate the positive and negative number
int separate (int arr[], int size)
{
    int j = 0, i , temp;
    for(i = 0; i < size; i++)
    {
    if (arr[i] <= 0)
    {
        /*Here we using bitwise operator to swap the
        numbers instead of using the temp variable*/
         arr[j] = arr[j]^arr[i];
         arr[i] = arr[j]^arr[i];
         arr[j] = arr[j]^arr[i];
         j++;
    }
    }
    printf("First We Separate the negetive and positive number \n");
    for( i = 0 ; i <size ; i++)
    {
        printf("Array[%d] = %d\n",i,arr[i]);
    }
    return j;
}
int findMissingPositive(int arr[], int size)
{
printf("Remove the negative numbers from array\n");
int i;
for( i = 0 ; i <size ; i++)
{
        printf("Array[%d] = %d\n",i,arr[i]);
}
for(i = 0; i < size; i++)
{
    if(abs(arr[i]) - 1 < size && arr[ abs(arr[i]) - 1] > 0)
    arr[ abs(arr[i]) - 1] = -arr[ abs(arr[i]) - 1];
}
for(i = 0; i < size; i++)
    if (arr[i] > 0)
    {
    return i+1;
    }
return size+1;
}
int findMissing(int arr[], int size)
{
int j = separate (arr, size);
return findMissingPositive(arr+j, size-j);
}
int main()
{
int size ;
printf("Enter the Value of Size of Array : ");
scanf("%d",&size);
int arr[size];
printf("Enter the values :\n");
for( int i = 0 ; i < size ; i++)
{
    printf("Array[%d] = ",i);
    scanf("%d",&arr[i]);
}
int missing = findMissing(arr,size);
printf("The smallest positive missing number is %d ", missing);
return 0;
}


输出

Enter the Value of Size of Array : 8
Enter the values :
Array[0] = 1
Array[1] = -1
Array[2] = -5
Array[3] = -3
Array[4] = 3
Array[5] = 4
Array[6] = 2
Array[7] = 8
First We Separate the negetive and positive number
Array[0] = -1
Array[1] = -5
Array[2] = -3
Array[3] = 1
Array[4] = 3
Array[5] = 4
Array[6] = 2
Array[7] = 8
Remove the negative numbers from array
Array[0] = 1
Array[1] = 3
Array[2] = 4
Array[3] = 2
Array[4] = 8
The smallest positive missing number is 5
Process returned 0 (0x0)   execution time : 27.914 s
Press any key to continue.

 /*
        How work :
        [if(abs(arr[i]) - 1 < size && arr[ abs(arr[i]) - 1] > 0)
        arr[ abs(arr[i]) - 1] = -arr[ abs(arr[i]) - 1];]
        before: arr = { 7, 3, 4, 5, 5, 3, 2}
    i == 0: arr[0] = 7
            arr[7-1] is 2 > 0 ~> negate
            arr = { 7, 3, 4, 5, 5, 3, -2}
    i == 1: arr[1] = 3
            arr[3-1] is 4 > 0 ~> negate
            arr = { 7, 3, -4, 5, 5, 3, -2}
    i == 2: arr[2] is -4 ~> abs for indexing
            arr[4-1] is 5 > 0 ~> negate
            arr = { 7, 3, -4,-5, 5, 3, -2}
    i == 3: arr[3] is -5 ~> abs for indexing
            arr[5-1] is 5 > 0 ~> negate
            arr = { 7, 3, -4, -5, -5, 3, -2}
    i == 4: arr[4] is -5 ~> abs for indexing
            arr[5-1] is -5 < 0 ~> print abs(-5) as duplicate
    i == 5: arr[5] is 3
            arr[3-1] is -4 < 0 ~> print abs(3) as duplicate
    i == 6: arr[6] is -2 ~> abs for indexing
            arr[2-1] is 3 > 0 ~> negate
            arr = { 7, -3, -4, -5, -5, 3, -2}

            indices of positive entries: 0, 5 ~> 1 and 6 not in original array
            indices of negative entries: 1, 2, 3, 4, 6 ~> 2, 3, 4, 5, 7 in original array
*/

如果将负值移到右边而不是左边会更容易:#include <stdio.h> int main (void) { int data[] = { -9, 7, 5, 0, 6, 2000, 1, 3, -1, 1, 9, -9 }; int i=0, size = sizeof(data)/sizeof(*data); do { while ((data[i] <= 0) && (i <= (size-1))) data[i] = data[--size -1]; } while (++i < size); for (i = 0; i < size; i++) if (((1 <= data[i]) && (data[i] <= size)) && (data[data[i]-1] > 0)) data[data[i]-1] *= -1; size+=1; for (i = 1; i < size; i++) if (data[i-1] > 0) {size = i; break;} printf ("%d\n", size); fflush(stdout); return(0); } - Graham Toal

0

这是另一种Python实现,具有O(n)时间复杂度和O(1)空间复杂度。

def segregate(arr):
    length = len(arr)
    neg_index = length
    for i, value in enumerate(arr):
        if(value < 1 and neg_index == length):
           neg_index = i
        if(neg_index != length and value >= 1):
           temp = arr[i]
           arr[i] = arr[neg_index]
           arr[neg_index] = temp
           neg_index += 1
    return arr[:neg_index]

def missingPositiveNumber(arr):
    arr = segregate(arr)
    length = len(arr)
    for i, value in enumerate(arr):
        if(value - 1 < l):
           arr[abs(value) - 1] = -(abs(arr[abs(value) - 1]))
    for i, value in enumerate(arr):
        if(value > 0):
           return i + 1
    return length + 1


print(missingPositiveNumber([1, -1, 2, 3]))

0

这是Java语言。时间复杂度为O(N),空间复杂度为O(1)。

private static int minimum_positive_integer(int[] arr) {
        int i = 0;
        int j = arr.length - 1;

        //splitting array
        while (i < j) {
            if (arr[i] > 0) {
                i++;
            }

            if (arr[j] <= 0) {
                j--;
            }

            if (arr[i] <= 0 && arr[j] > 0) {
                int t = arr[i];
                arr[i] = arr[j];
                arr[j] = t;

                i++;
                j--;
            }
        }
        int len_positive = i;

        if (arr[i] > 0) len_positive++;

        for (i = 0; i < len_positive; i++) {
            int abs = Math.abs(arr[i]);
            if (abs <= len_positive) {
                int index = abs - 1;
                arr[index] = -abs;
            }
        }

        for (i = 0; i < len_positive; i++) {
            if(arr[i] > 0) return  i + 1;
        }

        return len_positive + 1;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接