![](/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.