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

Posted

技术标签:

【中文标题】Python 给定一个包含 N 个整数的数组 A,返回在 O(n) 时间复杂度内不会出现在 A 中的最小正整数(大于 0)【英文标题】:Python given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A in O(n) time complexity 【发布时间】:2018-08-19 19:28:40 【问题描述】:

例如:

输入:A = [ 6 4 3 -5 0 2 -7 1 ]

输出:5

因为 5 是未出现在数组中的最小正整数。


我已经为这个问题写了两个解决方案。第一个很好,但我不想使用任何外部库 + 它的 O(n)*log(n) 复杂性。当输入是混沌序列长度=10005(带减号)时,第二种解决方案“我需要你的帮助来优化它”会出错。

解决方案 1:

from itertools import count, filterfalse 


def minpositive(a):
    return(next(filterfalse(set(a).__contains__, count(1))))

解决方案 2:

def minpositive(a):
    count = 0
    b = list(set([i for i in a if i>0]))
    if min(b, default = 0)  > 1 or  min(b, default = 0)  ==  0 :
        min_val = 1
    else:
        min_val = min([b[i-1]+1 for i, x in enumerate(b) if x - b[i - 1] >1], default=b[-1]+1)
        
    return min_val

注意:这是一个关于 codility 的演示测试,解决方案 1 获得了 100% 和 解决方案 2 得到了 77 %。 “解决方案 2”中的错误是由于: 性能测试-> 中等混沌序列长度 = 10005(带减号)预期为 3 10000 性能测试 -> large chaotic + many -1, 1, 2, 3 (with 减)得到 5 预期 10000

【问题讨论】:

我认为您假设 list(set(a)) 已排序,但事实并非如此。不清楚你在问什么——你是在问工作代码吗? 两者都在工作,但我正在寻找一种方法来优化该代码以使用 O(n) 时间复杂度“如我的问题所述”。 感谢Paul 的提示“我认为您正在假设 list(set(a)) ”。它不会影响我的第二个代码。我以后会用 sorted。 这是来自codility.com的演示任务:) 【参考方案1】:
def minpositive(A):
    """Given an list A of N integers, 
    returns the smallest positive integer (greater than 0) 
    that does not occur in A in O(n) time complexity

        Args:
            A: list of integers
        Returns:
            integer: smallest positive integer

        e.g:
            A = [1,2,3]
            smallest_positive_int = 4
    """
    len_nrs_list = len(A)
    N = set(range(1, len_nrs_list+2))
    
    return min(N-set(A)) #gets the min value using the N integers
    

【讨论】:

如果你以N = set(range(1, len_nrc_list+2))开头,你可以摆脱try/except。【参考方案2】:
def solution(A):
    nw_A = sorted(set(A))
    if all(i < 0 for i in nw_A):
        return 1
    else:
        ans = 1
        while ans in nw_A:
            ans += 1
            if ans not in nw_A:
                return ans

如果有可能导入 numpy 包,以获得更好的性能。

def solution(A):
    import numpy as np
    nw_A = np.unique(np.array(A))
    if np.all((nw_A < 0)):
        return 1
    else:
        ans = 1
        while ans in nw_A:
            ans += 1
            if ans not in nw_A:
                return ans

【讨论】:

每次遇到if 语句时都会调用sorted 吗?另外,是sorted O(n) 吗? 您将数组排序最多 N 次,因此您的时间复杂度为 O(n^2 * log n),远远超出标准! 谢谢 Calculuswhiz 和 Joanis,我更正了【参考方案3】:

这个问题真的不需要另一个答案,但是有一个尚未提出的解决方案,我相信它比目前提出的解决方案要快。

正如其他人所指出的,我们知道答案在[1, len(A)+1] 范围内,包括在内。我们可以把它变成一个集合,取集合中与 A 差的最小元素。这是一个很好的 O(N) 解决方案,因为集合操作是 O(1)。

但是,我们不需要使用 Python 集来存储 [1, len(A)+1],因为我们从密集集开始。我们可以使用数组来代替,它将通过列表索引替换集合散列,并为我们提供另一个具有较低常数的 O(N) 解决方案。

def minpositive(a):
    # the "set" of possible answer - values_found[i-1] will tell us whether i is in a
    values_found = [False] * (len(a)+1)
    # note any values in a in the range [1, len(a)+1] as found
    for i in a:
        if i > 0 and i <= len(a)+1:
            values_found[i-1] = True
    # extract the smallest value not found
    for i, found in enumerate(values_found):
        if not found:
            return i+1

我们知道,最终的 for 循环总是会找到一个未标记的值,因为它比 a 多一个元素,所以它的至少一个单元格没有设置为 True

【讨论】:

不确定它是否适用于此解决方案,但如果您有一个整数列表,例如:a = [2,3,2],它会返回 2 个值 -> 1, 4 @mseromenho 我不确定你的意思。我刚刚测试了print(minpositive([2,3,2])),它给了我1 我的错,我错误地测试了代码,我直接从 for 循环打印!很好的解决方案!【参考方案4】:

在比较之前我减少了集合的长度

a=[1,222,3,4,24,5,6,7,8,9,10,15,2,3,3,11,-1]
#a=[1,2,3,6,3]
def sol(a_array):
    a_set=set()
    b_set=set()
    cnt=1
    for i in a_array:

        #In order to get the greater performance
        #Checking if element is greater than length+1 
        #then it can't be output( our result in solution)
        
        if i<=len(a) and i >=1:
            
            a_set.add(i) # Adding array element in set 
            b_set.add(cnt) # Adding iterator in set
            cnt=cnt+1
    b_set=b_set.difference(a_set)
    if((len(b_set)) > 1): 
        return(min(b_set))
    else:
        return max(a_set)+1

sol(a)  

【讨论】:

【参考方案5】:

从 Niroj Shrestha 和 najeeb-jebreel 继续,添加了一个初始部分以避免在完整集的情况下进行迭代。如果数组非常大,则尤其重要。

def smallest_positive_int(A):
  sorted_A = sorted(A)
  last_in_sorted_A = sorted_A[-1]
  #check if straight continuous list
  if len(sorted_A) == last_in_sorted_A:
    return last_in_sorted_A + 1
  else:
    #incomplete list, iterate to find the smallest missing number
    sol=1
    for x in sorted_A:
        if x == sol:
          sol += 1
        else:
          break
    return sol

A = [1,2,7,4,5,6]
print(smallest_positive_int(A))

【讨论】:

如果A中有重复,此解决方案不起作用。【参考方案6】:

我刚刚修改了@najeeb-jebreel 的答案,现在该函数给出了最佳解决方案。

def solution(A):
    sorted_set = set(sorted(A))
    sol = 1
    for x in sorted_set:
        if x == sol:
            sol += 1
        else:
            break
    return sol

【讨论】:

【参考方案7】:
def solution(A):
    arr = set(A)
    N = set(range(1, 100001))
    while N in arr:
       N += 1
    return min(N - arr)

solution([1, 2, 6, 4])
#returns 3

【讨论】:

【参考方案8】:

对于大型阵列来说速度很快。

def minpositive(arr):
    if 1 not in arr: # protection from error if ( max(arr) < 0 )
        return 1
    else:
        maxArr = max(arr) # find max element in 'arr'
        c1 = set(range(2, maxArr+2)) # create array from 2 to max
        c2 = c1 - set(arr) # find all positive elements outside the array
        return min(c2)

【讨论】:

【参考方案9】:

如果 N 的范围是给定的,以下也可以:

N = set(range(1, 100001))
def minpositive(A):
    return min(N-set(A))

【讨论】:

您可以假设范围是range(1, len(A)+1),因为您确定答案在该范围内,无论 A 中的值如何。【参考方案10】:
def solution(A):
    B = set(sorted(A))
    m = 1
    for x in B:
        if x == m:
            m+=1
    return m

【讨论】:

请提供解释以说明您的代码如何解决问题。 OP 说明这里的时间复杂度应该是线性时间,这里提供的答案是二次时间。 @avizzzy,不是O(n * log n)吗?如果 sorted 调用被删除,它可能是 O(n),毕竟所有集合都是无序的。 Python 集不保留插入项目的顺序,因此该解决方案不应该工作。在实践中,似乎 Python 会针对相当大的最大值对整数集进行排序,但这可能是集合实现方式的结果,现在它是您可以信赖的功能。【参考方案11】:

在 Python 中测试集合中是否存在数字很快,因此您可以尝试以下操作:

def minpositive(a):
    A = set(a)
    ans = 1
    while ans in A:
       ans += 1
    return ans

【讨论】:

这也会处理给定列表中可能存在的任何重复项。

以上是关于Python 给定一个包含 N 个整数的数组 A,返回在 O(n) 时间复杂度内不会出现在 A 中的最小正整数(大于 0)的主要内容,如果未能解决你的问题,请参考以下文章

Python给定一个整数n,将1到n之间的正整数按偶数递增,技术递减的顺序输出?

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

2022-03-06:金币路径。 给定一个数组 A(下标从 1 开始)包含 N 个整数:A1,A2,……,AN 和一个整数 B。 你可以从数组 A 中的任何一个位置(下标为 i)跳到下标 i+1,i+

[Python]CCF——数字排序(201503-2)

2021-11-01:寻找重复数。给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设 nums 只有 一个重复的整数

树状数组模板题 P1904