我在python中有一个列表:
myList = [1,14,2,5,3,7,8,12]
如何轻松找到第一个未使用的值? (在这种情况下'4')
答案 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 A
。 count(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
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