找到列表中不存在的最小正数

时间:2015-01-27 17:53:32

标签: python list

我在python中有一个列表:

myList = [1,14,2,5,3,7,8,12]

如何轻松找到第一个未使用的值? (在这种情况下'4')

15 个答案:

答案 0 :(得分:16)

我想出了几种不同的方式:

迭代第一个不在集合

中的数字

我不想获得最短的代码(可能是设置差异技巧),但是可以有一个很好的运行时间。

这可能是这里提出的最佳建议之一,我的测试表明它可能会快得多 - 特别是如果这个洞在开头 - 而不是设定差异方法:

from itertools import count, filterfalse # ifilterfalse on py2

A = [1,14,2,5,3,7,8,12]
print(next(filterfalse(set(A).__contains__, count(1))))

数组变为set,其__contains__(x)方法对应x in Acount(1)创建一个计数器,从1开始计数到无穷大。现在,filterfalse使用计数器中的数字,直到找到一个不在集合中的数字;当找到第一个不在集合中的数字时,它由next()

产生

len(a) = 100000的时间安排,随机数和受欢迎的数字为8

>>> timeit(lambda: next(filterfalse(set(a).__contains__, count(1))), number=100)
0.9200698399945395
>>> timeit(lambda: min(set(range(1, len(a) + 2)) - set(a)), number=100)
3.1420603669976117

len(a) = 100000的时间安排,有序且第一个免费为100001

>>> timeit(lambda: next(filterfalse(set(a).__contains__, count(1))), number=100)
1.520096342996112
>>> timeit(lambda: min(set(range(1, len(a) + 2)) - set(a)), number=100)
1.987783643999137

(请注意,这是Python 3,range是py2 xrange

使用heapq

渐近的好答案:heapq enumerate

from heapq import heapify, heappop

heap = list(A)
heapify(heap)

from heapq import heapify, heappop
from functools import partial

# A = [1,2,3] also works
A = [1,14,2,5,3,7,8,12]

end = 2 ** 61      # these are different and neither of them can be the 
sentinel = 2 ** 62 # first gap (unless you have 2^64 bytes of memory).

heap = list(A)
heap.append(end)
heapify(heap)

print(next(n for n, v in enumerate(
     iter(partial(heappop, heap), sentinel), 1) if n != v))

现在,如果用C语言编写,上面的那个可能是首选的解决方案,但heapq是用Python编写的,并且很可能比主要使用C代码的许多其他替代方案慢。

只需排序并枚举以找到第一个不匹配的

或者对于O(n lg n)

具有良好常数的简单答案
next(i for i, e in enumerate(sorted(A) + [ None ], 1) if i != e)

这可能是最快的如果列表几乎按照Python Timsort的工作原理进行排序,但对于随机化,设置差异和迭代第一个不在集合中的速度更快。

+ [ None ]对于没有间隙的边缘情况是必要的(例如[1,2,3])。

答案 1 :(得分:4)

我建议您使用生成器并使用枚举来确定缺少的元素

>>> next(a for a, b in enumerate(myList, myList[0]) if a != b)
4

enumerate使用元素映射索引,因此您的目标是确定与索引不同的元素。 注意,我也假设元素可能不是以一个确定的值开始,在这种情况下是1,如果是这样,你可以进一步简化表达式

>>> next(a for a, b in enumerate(myList, 1) if a != b)
4

答案 2 :(得分:4)

不知道效率如何,但为什么不使用xrange作为掩码并使用set minus?

>>> myList = [1,14,2,5,3,7,8,12]
>>> min(set(xrange(1, len(myList) + 1)) - set(myList))
4

你只是创建一个与myList一样大的集合,所以它不会那么糟糕:)

这不适用于“完整”列表:

>>> myList = range(1, 5)
>>> min(set(xrange(1, len(myList) + 1)) - set(myList))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence

但是返回下一个值的修复很简单(再添加一个掩码集):

>>> min(set(xrange(1, len(myList) + 2)) - set(myList))
5

答案 3 :(得分:2)

这利用了集合

的属性
>>> l = [1,2,3,5,7,8,12,14]
>>> m = range(1,len(l))
>>> min(set(m)-set(l))
4

答案 4 :(得分:1)

天真的方法是遍历列表,这是一个O(n)解决方案。但是,由于列表已排序,您可以使用此功能执行二进制搜索(它的修改版本)。基本上,你正在寻找A [i] = i。

的最后一次出现

伪算法将类似于:

binarysearch(A):
  start = 0
  end = len(A) - 1
  while(start <= end ):
    mid = (start + end) / 2
    if(A[mid] == mid):
      result = A[mid]
      start = mid + 1
    else: #A[mid] > mid since there is no way A[mid] is less than mid
      end = mid - 1
  return (result + 1)

这是一个O(log n)解决方案。我假设列表是一个索引。您可以相应地修改索引

编辑:如果列表没有排序,你可以使用heapq python库并将列表存储在最小堆中,然后逐个弹出元素

伪代码

H = heapify(A) //Assuming A is the list
count = 1
for i in range(len(A)):
  if(H.pop() != count): return count
  count += 1

答案 5 :(得分:1)

import itertools as it

next(i for i in it.count() if i not in mylist)

我喜欢这个,因为它非常接近你正在尝试做的事情:“开始计算,继续前进,直到你找到一个不在列表中的数字,然后告诉我这个数字”。但是,这是二次的,因为测试i not in mylist是线性的。

使用枚举的解决方案是线性的,但依赖于正在排序的列表,并且没有重复的值。首先排序使其总体为O(n log n),这仍然优于二次方。但是,如果您可以假设值是不同的,那么您可以先将它们放入一个集合中:

myset = set(mylist)
next(i for i in it.count() if i not in myset)

由于设置包含检查大致是恒定时间,因此整体上是线性的。

答案 6 :(得分:1)

带有列表的for循环将执行此操作。

l = [1,14,2,5,3,7,8,12]
for i in range(1, max(l)):
    if i not in  l: break
print(i) # result 4

答案 7 :(得分:0)

我的努力,没有itertools。将“current”设置为小于您期望值的值。

list = [1,2,3,4,5,7,8]
current = list[0]-1
for i in list:
    if i != current+1:
        print current+1
        break
    current = i

答案 8 :(得分:0)

排序+减少救援!

from functools  import reduce # python3
myList = [1,14,2,5,3,7,8,12]
res = 1 + reduce(lambda x, y: x if y-x>1 else y, sorted(myList), 0)
print(res)

不幸的是,在找到匹配后它不会停止,并将迭代整个列表。

更快(但不那么有趣)是用于循环:

myList = [1,14,2,5,3,7,8,12]
res = 0
for num in sorted(myList):
    if num - res > 1:
        break
    res = num
res = res + 1
print(res)

答案 9 :(得分:0)

您可以尝试

for i in range(1,max(arr1)+2):
        if i not in arr1:
            print(i)
            break

答案 10 :(得分:0)

最简单的方法是循环浏览排序后的列表,并检查索引是否等于该值,如果不返回索引作为解决方案。 由于排序,其复杂度为O(nlogn):

for index,value in enumerate(sorted(myList)):
        if index is not value:
            return index

另一种选择是使用python集,它们有些字典中没有值,只有键。在词典中,您可以恒定的时间查找关键字,这使得whol解决方案如下所示,仅具有线性复杂度O(n):

mySet = set(myList)
for i in range(len(mySet)):
    if i not in mySet:
        return i

答案 11 :(得分:0)

我只是以一种非pythonic的方式解决了这个问题

def solution(A):
    # Const-ish to improve readability
    MIN = 1
    if not A: return MIN
    # Save re-computing MAX
    MAX = max(A)
    # Loop over all entries with minimum of 1 starting at 1
    for num in range(1, MAX):
        # going for greatest missing number return optimistically (minimum)
        # If order needs to switch, then use max as start and count backwards
        if num not in A: return num
    # In case the max is < 0 double wrap max with minimum return value
    return max(MIN, MAX+1)

我认为它读起来很好

答案 12 :(得分:0)

易于阅读,易于理解,可以完成工作:

<Style TargetType="Ellipse" x:Key="ellipseStyle">
    <Setter Property="Height" Value="10" />
    <Setter Property="Width" Value="10" />
    <Style.Triggers>            
        <DataTrigger Binding="{Binding Key}" Value="0"><!-- "0" - one of the dictionary key -->
            <Setter Property="Fill" Value="Red" />
        </DataTrigger>          
        <DataTrigger Binding="{Binding Key}" Value="1"><!-- "1" - one of the dictionary key -->
            <Setter Property="Fill" Value="Green" />
        </DataTrigger>
    </Style.Triggers>
</Style>

<Style TargetType="{x:Type ComboBox}" x:Key="cmb_osn_rez">
    <Setter Property="ItemsSource" Value="{Binding MyDict}" />
    <Setter Property="SelectedValuePath" Value="Key" />
    <Setter Property="ItemsControl.ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Ellipse Style="{StaticResource ellipseStyle}" />
                    <TextBlock Text="{Binding Value}" />
                </StackPanel>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

答案 13 :(得分:-1)

返回所有这些值的解决方案是

free_values = set(range(1, max(L))) - set(L)

它执行完整扫描,但这些循环是用C实现的,除非列表或其最大值很大,否则这将胜过在Python中执行循环的更复杂的算法。

请注意,如果需要进行此搜索以实现ID的“重用”,那么保留一个空闲列表并使其保持最新(即在删除条目时添加数字并在重用条目时从中添加数字)是通常是一个好主意。

答案 14 :(得分:-1)

以下解决方案循环介于1和输入列表长度之间的所有数字,并在其中找不到数字时中断循环。否则结果是列表的长度加一。

listOfNumbers=[1,14,2,5,3,7,8,12]
for i in range(1, len(listOfNumbers)+1):
   if not i in listOfNumbers: 
      nextNumber=i
      break
else:
   nextNumber=len(listOfNumbers)+1