【问题标题】: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 complexityPython 给定一个包含 N 个整数的数组 A,返回在 O(n) 时间复杂度内不会出现在 A 中的最小正整数(大于 0)
【发布时间】: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的演示任务:)

标签: python python-3.x algorithm time-complexity


【解决方案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
          

          【讨论】:

          • 这也会处理给定列表中可能存在的任何重复项。
          猜你喜欢
          • 2021-04-21
          • 2022-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多