返回 Array 中未出现的最小正整数(大于 0)

Posted

技术标签:

【中文标题】返回 Array 中未出现的最小正整数(大于 0)【英文标题】:returns the smallest positive integer (greater than 0) that does not occur in Array 【发布时间】:2021-05-28 07:34:58 【问题描述】:

我有以下问题:-

写一个函数:

class Solution  public int solution(int[] A); 

给定一个包含 N 个整数的数组 A,返回在 A 中没有出现的最小正整数(大于 0)。

例如,给定 A = [1, 3, 6, 4, 1, 2],函数应该返回 5。

给定 A = [1, 2, 3],函数应该返回 4。

给定 A = [−1, −3],函数应该返回 1。

为以下假设编写一个有效的算法:

N 是 [1..100,000] 范围内的整数; 数组 A 的每个元素都是 [−1,000,000..1,000,000] 范围内的整数。

现在我尝试了这段代码:-

using System;
// you can also use other imports, for example:
// using System.Collections.Generic;

// you can write to stdout for debugging purposes, e.g.
// Console.WriteLine("this is a debug message");

class Solution

    public int solution(int[] A)
    
        // write your code in C# 6.0 with .NET 4.5 (Mono)
        int n = 1;
        Array.Sort(A);
        for (int i = 1; i <= 100000; i++)
        
            for (int i2 = 0; i2 <= A.Length - 1; i2++)
            
                if (A[i2] == i)

                
                    n = A[i2] + 1;
                    break;
                

            



        
        return n;

    

我的代码在这些测试数据上运行良好:-

A = [1, 2, 3]

A = [-1, -3]

虽然失败了:-

A = [1, 3, 6, 4, 1, 2] 返回 7 而不是 5。

有什么建议为什么我的代码在第三次测试中失败了?

谢谢

【问题讨论】:

@TimSchmelter 所以有什么建议可以解决这个问题吗? 最好的方法不是(1)像你已经做的那样排序,(2)二进制印章以找到值 1 或 1 所在的最近位置。 (3) 从该索引开始线性搜索以找到第一个“差距”(处理缺少 1 的情况,因此是答案) @MatthewWatson 能否提供示例代码 我只是认为这样会更好,但是对 N 的限制让我得出结论,有一个更好更聪明的算法。这值得考虑更多。 内部 (i2) 循环检测 i 是否是 A 数组的一部分。一旦找到,它就会中断,然后下一个i 会被占用。这里的问题是外循环总是运行到完成,所以在某些时候i 是 6,而内循环找到了一个匹配项,覆盖了n 的正确值。您可能想在分配新值之前检查ncurrent 值,看看是否存在差距? (注意我不是在这里讨论效率) 【参考方案1】:

我将使用以下方法,使用 HashSet&lt;int&gt; 检查给定的整数是否丢失:

public static int? SmallestMissing(int[] A, int rangeStart = 1, int rangeEnd = 100_000)

     HashSet<int> hs = new HashSet<int>(A);
     for (int i = rangeStart; i <= rangeEnd; i++)
        if(!hs.Contains(i)) return i;
     
     return null;

HashSet 是唯一值的集合,它在查找项目时非常有效(复杂度为 O(1))。因此,您会以一些内存为代价获得一个非常易读且高效的算法。

也许你可以通过提供另一种算法来优化它,以防数组非常大,你不想冒险OutOfMemoryException

public static int? SmallestMissing(int[] A, int rangeStart = 1, int rangeEnd = 100_000)

    if(A.Length > 1_000_000)
    
        Array.Sort(A);
        for (int i = rangeStart; i <= rangeEnd; i++)
        
            int index = Array.BinarySearch(A, i);
            if(index < 0) return i;
        
        return null;
    
    
    HashSet<int> hs = new HashSet<int>(A);
    for (int i = rangeStart; i <= rangeEnd; i++)
        if(!hs.Contains(i)) return i;
 
    return null;
 

【讨论】:

但它应该返回 i+1 吗?而不是我? @JohnJohn:不,OP 想要“返回未出现在数组中的最小正整数”。我想这种误解来自于 OP 的方法,即首先订购该大数组并在嵌套循环中对其进行迭代。 @TimSchmelter 他 OP @C.Evenhuis:公平点:D 不,您不需要返回数字+1,因为您迭代所有可能的数字并简单地检查它们是否包含在集合中。无需订购整个数组并始终循环它。【参考方案2】:
using System.Linq;

int smallestNumber = Enumerable.Range(1, 100000).Except(A).Min();

【讨论】:

请注意,这种方法确实创建了一个包含 100K 元素的集合——这可能违反了 OP 的“高效”要求。显然 OP 已将此标记为答案,但我们可能需要等待看它是否通过了老师的作业;) @MatthewWatson 在保持简单、干净和易于理解方面非常高效。 ?? Enumerable.Range(1, A.Length + 1) 我猜应该足够了..【参考方案3】:

如果您被允许对数组进行就地排序,这意味着修改输入参数值,这里有一个对缺失值的简单线性探测(当然是排序的顶部)。

这是伪代码:

Sort the array
Skip all negatives and 0's at the start
Loopify the following:
    Expect 1, if not found at current location return 1
    Skip all 1's
    Expect 2, if not found at current location return 2
    Skip all 2's
    Expect 3, if not found at current location return 3
    Skip all 3's
    ... and so on for 4, 5, 6, etc. until end of array
If we get here, return currently expected value which should've been at the end

代码如下:

public static int FirstMissingValue(int[] input)

    Array.Sort(input);
    
    int index = 0;
    
    // Skip negatives
    while (index < input.Length && input[index] < 1)
        index++;

    int expected = 1;
    while (index < input.Length)
    
        if (input[index] > expected)
            return expected;
            
        // Skip number and all duplicates
        while (index < input.Length && input[index] == expected)
            index++;
            
        expected++;
    
    
    return expected;

测试用例:

Console.WriteLine(FirstMissingValue(new[]  1, 3, 6, 4, 1, 2 ));
Console.WriteLine(FirstMissingValue(new[]  1, 2, 3 ));
Console.WriteLine(FirstMissingValue(new[]  -1, -3 ));

输出:

5
4
1

【讨论】:

【参考方案4】:

如果输入数组变成这样,您的算法将不起作用:[1,2-1,1,3,5]。我是根据你的算法做的。试试看:

int[] a = new int[]  -1, -2;

        IEnumerable<int> uniqueItems = a.Distinct<int>().Where(x => x > 0);
        
        if (uniqueItems.Count() == 0)
        
            Console.WriteLine("result: 1");
        
        else
        
            Array asList = uniqueItems.ToArray();
            Array.Sort(asList);

            for (int i = 1; i <= 100000; i++)
            
                if ((int)asList.GetValue(i - 1) != i)

                
                    Console.WriteLine("result: " + i);
                    break;
                
            
        

【讨论】:

这是 C#,不是 javascript @JohnathanBarclay 抱歉,我已修复答案【参考方案5】:

你可以这样试试。

    public static int solution(int[] A)
    
        int smallest = -1;
        Array.Sort(A);
        if(A[0] > 1)
            return 1;
        for(int i = 0; i < A.Length; i++)
        
          
            if(A.Length != i+1 && A[i] + 1 != A[i + 1] && A[i+1] > 0)
            
                smallest = A[i]+1;
                break;
            
            else if(A[i] > 0 && A.Length == i+1)
            
                smallest = A[i] + 1;
            
        
        return smallest > 0 ? smallest:1;
    

【讨论】:

【参考方案6】:

这是使用O(N) 分区后跟O(N) 搜索的方法。这种方法不使用任何额外的存储,但它确实改变了数组的内容。

此代码已转换为from here。另见this article。

我添加了 cmets 来尝试解释第二阶段 findSmallestMissing() 的工作原理。我没有评论分区方法,因为它只是标准分区的一种变体,可能用于快速排序算法。

static class Program

    public static void Main()
    
        Console.WriteLine(FindSmallestMissing(1, 3, 6, 4, 1, 2));
        Console.WriteLine(FindSmallestMissing(1, 2, 3));
        Console.WriteLine(FindSmallestMissing(-1, -3));
    

    public static int FindSmallestMissing(params int[] array)
    
        return findSmallestMissing(array, partition(array));
    

    // Places all the values > 0 before any values <= 0,
    // and returns the index of the first value <= 0.
    // The values are unordered.

    static int partition(int[] arr)
    
        void swap(int x, int y)
        
            var temp = arr[x];
            arr[x]   = arr[y];
            arr[y]   = temp;
        

        int pIndex = 0; // Index of pivot.

        for (int i = 0; i < arr.Length; i++)
        
            if (arr[i] > 0) // pivot is 0, hence "> 0"
                swap(i, pIndex++);
        

        return pIndex;
    

    // This is the clever bit.
    // We will use the +ve values in the array as flags to indicate that the number equal to that index is
    // present in the array, by making the value negative if it is found in the array.
    // This way we can store both the original number AND whether or not the number equal to that index is present
    // in a single value.
    //
    // Given n numbers that are all > 0, find the smallest missing number as follows:
    //
    // For each array index i in (0..n):
    //     val = |arr[i]| - 1;          // Subtract 1 so val will be between 0 and max +ve value in original array.
    //     if (val is in range)         // If val beyond the end of the array we can ignore it
    //     and arr[val] is non-negative // If already negative, no need to make it negative.
    //        make arr[val] negative
    //
    // After that stage, we just need to find the first positive number in the array, which indicates that
    // the number equal to that index + 1 is missing.

    // n = number of values at the start of the array that are > 0
    static int findSmallestMissing(int[] arr, int n)
    
        for (int i = 0; i < n; i++)
        
            int val = Math.Abs(arr[i]) - 1;

            if (val < n && arr[val] >= 0)
                arr[val] = -arr[val];
        

        for (int i = 0; i < n; i++)
        
            if (arr[i] > 0)   // Missing number found.
                return i + 1;
        

        return n + 1; // No missing number found.
    

【讨论】:

【参考方案7】:
class Program

    static void Main(string[] args)
    
        int [] A = new int[]  1, 2, 3;
        int n = 0;
        bool found = false;
        Array.Sort(A);
        for (int i = 1; i <= 100000; i++) 
            for (int x = 0; x <= A.Length - 1; x++) 
                int next = (x + 1) < A.Length ? (x + 1): x;
                if (A[x] > 0 && (A[next] - A[x]) > 0) 
                    n = A[x] + 1;
                    found = true;
                    break;
                
            

            if(found) 
                break;
            
        
        Console.WriteLine("Smallest number: " + n);

    

【讨论】:

你能补充一些解释吗?【参考方案8】:
 int smallestNumber=Enumerable.Range(1,(int.Parse(A.Length.ToString())+1)).Except(A).Min();
    Array.Sort(A);
    for (int number = 1; number <= 100000; number++)
    
        for (int num = number; i2 <= A.Length - 1; num++)
        
            if (A[num] == number)

            
                smallestNumber = A[num] + 1;
                break;
            

        
     
    return smallestNumber; 

【讨论】:

【参考方案9】:
using System.Linq;

class Solution 
    public int solution(int[] A) 
        int smallestNumber = Enumerable.Range(1, 100000).Except(A).Min();
        return smallestNumber;
    

【讨论】:

【参考方案10】:

最简单的一个:)

class Solution

    public int solution(int[] array)
    
        int[] onlyPositiveArray = array.Where(a => a > 0).OrderBy(a => a).Distinct().ToArray();

        int smallestNumber = 1;
        foreach (var number in onlyPositiveArray)
        
            if (smallestNumber  != number)
            
                break;
            

            smallestNumber ++;
        

        if (!onlyPositiveArray.Contains(smallestNumber ))
        
            return smallestNumber;
        
        else
        
            return smallestNumber  + 1;
        
    

【讨论】:

以上是关于返回 Array 中未出现的最小正整数(大于 0)的主要内容,如果未能解决你的问题,请参考以下文章

查找数组中未出现的最小正整数

求无序数组中未出现的最小正整数

8.25 数组中未出现的最小正整数

Leetcode刷题Python牛客. 数组中未出现的最小正整数

Python 给定一个包含 N 个整数的数组 A,返回在 O(n) 时间复杂度内不会出现在 A 中的最小正整数(大于 0)

算法总结之 数组中未出现的最小正整数