![](/img/trans.png)
[英]Find smallest positive number in a list without using a built-in function
[英]Find the smallest positive number not in list
我在 python 中有一个这样的列表:
myList = [1,14,2,5,3,7,8,12]
如何轻松找到第一个未使用的值? (在本例中为“4”)
我想出了几种不同的方法:
我不想得到最短的代码(这可能是 set-difference 诡计),而是一些可能有良好运行时间的东西。
这可能是这里提出的最好的方法之一,我的测试表明它可能比 set-difference 方法快得多 - 特别是如果洞在开始时 - :
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
with 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]
)是必要的。
这利用了集合的属性
>>> l = [1,2,3,5,7,8,12,14]
>>> m = range(1,len(l))
>>> min(set(m)-set(l))
4
我建议您使用生成器并使用 enumerate 来确定缺少的元素
>>> 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
不知道效率如何,但为什么不使用 xrange 作为掩码并使用 set减号?
>>> myList = [1,14,2,5,3,7,8,12]
>>> min(set(xrange(1, len(myList) + 1)) - set(myList))
4
您只创建了一个与myList
一样大的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
import itertools as it
next(i for i in it.count() if i not in mylist)
我喜欢这个,因为它与你想要做的事情非常接近:“开始计数,继续计算,直到你找到一个不在列表中的数字,然后告诉我那个数字”。 然而,这是二次的,因为测试i not in mylist
是线性的。
使用 enumerate 的解决方案是线性的,但依赖于正在排序的列表并且没有重复的值。 先排序使其整体为 O(n log n),这仍然比二次方好。 但是,如果您可以假设这些值是不同的,那么您可以先将它们放入一个集合中:
myset = set(mylist)
next(i for i in it.count() if i not in myset)
由于设置包含检查的时间大致恒定,因此总体上将是线性的。
带有列表的 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
我只是以一种可能非 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)
我认为它读得很好
我的努力,没有itertools。 将“当前”设置为小于您期望的值。
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
最简单的方法是遍历 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
排序+减少来拯救!
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)
不幸的是,它不会在找到匹配项后停止,而是会迭代整个列表。
更快(但不那么有趣)是使用 for 循环:
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)
你可以试试这个
for i in range(1,max(arr1)+2):
if i not in arr1:
print(i)
break
最简单的方法是循环遍历排序列表并检查索引是否等于值,如果不等于将索引作为解决方案返回。 由于排序,这将具有复杂度 O(nlogn):
for index,value in enumerate(sorted(myList)):
if index is not value:
return index
另一种选择是使用 python 集,它有点像没有值的字典,只是键。 在字典中,您可以在恒定时间内寻找一个键,这使得整个解决方案如下所示,只有线性复杂度 O(n):
mySet = set(myList)
for i in range(len(mySet)):
if i not in mySet:
return i
在循环中不断递增计数器,直到找到第一个不在列表中的正整数。
def getSmallestIntNotInList(number_list):
"""Returns the smallest positive integer that is not in a given list"""
i = 0
while True:
i += 1
if i not in number_list:
return i
print(getSmallestIntNotInList([1,14,2,5,3,7,8,12]))
# 4
我发现与这篇文章中的其他答案相比,这具有最快的性能。 我在 Python 3.10.8 中使用timeit
进行了测试。 我的性能结果如下所示:
import timeit
def findSmallestIntNotInList(number_list):
# Infinite while-loop until first number is found
i = 0
while True:
i += 1
if i not in number_list:
return i
t = timeit.Timer(lambda: findSmallestIntNotInList([1,14,2,5,3,7,8,12]))
print('Execution time:', t.timeit(100000), 'seconds')
# Execution time: 0.038100800011307 seconds
import timeit
def findSmallestIntNotInList(number_list):
# Loop with a range to len(number_list)+1
for i in range (1, len(number_list)+1):
if i not in number_list:
return i
t = timeit.Timer(lambda: findSmallestIntNotInList([1,14,2,5,3,7,8,12]))
print('Execution time:', t.timeit(100000), 'seconds')
# Execution time: 0.05068870005197823 seconds
import timeit
def findSmallestIntNotInList(number_list):
# Loop with a range to max(number_list) (by silgon)
# https://stackoverflow.com/a/49649558/3357935
for i in range (1, max(number_list)):
if i not in number_list:
return i
t = timeit.Timer(lambda: findSmallestIntNotInList([1,14,2,5,3,7,8,12]))
print('Execution time:', t.timeit(100000), 'seconds')
# Execution time: 0.06317249999847263 seconds
import timeit
from itertools import count, filterfalse
def findSmallestIntNotInList(number_list):
# iterate the first number not in set (by Antti Haapala -- Слава Україні)
# https://stackoverflow.com/a/28178803/3357935
return(next(filterfalse(set(number_list).__contains__, count(1))))
t = timeit.Timer(lambda: findSmallestIntNotInList([1,14,2,5,3,7,8,12]))
print('Execution time:', t.timeit(100000), 'seconds')
# Execution time: 0.06515420007053763 seconds
import timeit
def findSmallestIntNotInList(number_list):
# Use property of sets (by Bhargav Rao)
# https://stackoverflow.com/a/28176962/3357935
m = range(1, len(number_list))
return min(set(m)-set(number_list))
t = timeit.Timer(lambda: findSmallestIntNotInList([1,14,2,5,3,7,8,12]))
print('Execution time:', t.timeit(100000), 'seconds')
# Execution time: 0.08586219989228994 seconds
返回所有这些值的解决方案是
free_values = set(range(1, max(L))) - set(L)
它会进行全面扫描,但这些循环是用 C 实现的,除非列表或其最大值很大,否则这将胜过在 Python 中执行循环的更复杂的算法。
请注意,如果需要此搜索来实现 ID 的“重用”,那么保留一个空闲列表并使其保持最新状态(即在删除条目时添加数字并在重用条目时从中挑选)通常是一个很好的选择主意。
以下解决方案循环 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
易于阅读,易于理解,完成工作:
def solution(A):
smallest = 1
unique = set(A)
for int in unique:
if int == smallest:
smallest += 1
return smallest
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.