使用python在大小为N的未排序列表中获取k个最小数字的最快方法是什么? 对大数字列表进行排序,然后获得k个最小数字,或者通过在列表中找到最小值k次来获得k个最小数字,确保从搜索中删除找到的最小值是否更快在下次搜索之前?
答案 0 :(得分:9)
您可以使用堆队列;它可以在O(NlogK)时间内从大小为N的列表中提供K个最大或最小的数字。
Python标准库包含heapq
module,已完成heapq.nsmallest()
function已完成实施:
import heapq
k_smallest = heapq.nsmallest(k, input_list)
在内部,这会创建一个大小为K的堆,其中包含输入列表的前K个元素,然后迭代剩余的N-K个元素,将每个元素推送到堆中,然后弹出最大的元素。这样的推送和弹出需要记录K时间,使整个操作为O(NlogK)。
该函数还优化了以下边缘情况:
min()
函数代替,给出O(N)结果。更好的选择是使用introselect algorithm,它提供O(n)选项。我所知道的唯一实现是使用numpy.partition()
function:
import numpy
# assuming you have a python list, you need to convert to a numpy array first
array = numpy.array(input_list)
# partition, slice back to the k smallest elements, convert back to a Python list
k_smallest = numpy.partition(array, k)[:k].tolist()
除了要求安装numpy
之外,这还需要N个内存(与heapq
的K相比),因为为该分区创建了列表的副本。
如果你只想要索引,你可以使用,对于任何一种变体:
heapq.nsmallest(k, range(len(input_list)), key=input_list.__getitem__) # O(NlogK)
numpy.argpartition(numpy.array(input_list), k)[:k].tolist() # O(N)
答案 1 :(得分:3)
编辑:这假设列表是不可变的。如果列表是一个数组并且可以修改,则可以使用线性方法。
使用大小为O(n * log k)
的堆,您可以将复杂性降低到k + 1
。
k
元素放入最小堆中。Heapify可以在对数时间内完成,因此时间复杂度如上所述。
答案 2 :(得分:2)
您可以使用selection algorithm在O(kn)
中执行此操作。一旦kn >= n log n
,切换到排序。也就是说,选择算法上的常量往往比快速排序上的常量高很多,因此真的需要比较i (kn)
和j (n log n)
。在实践中,除非您处理大型 n 或非常小的 k ,否则通常更有必要进行排序。
编辑:见评论。它实际上是 lot 更好。
答案 3 :(得分:2)
您可能需要查看heapq
:
In [109]: L = [random.randint(1,1000) for _ in range(100)]
In [110]: heapq.nsmallest(10, L)
Out[110]: [1, 17, 17, 19, 24, 37, 37, 45, 63, 73]
答案 4 :(得分:2)
如果第k个最小数字的列表不需要排序,可以使用像introselect这样的选择算法在O(n)时间内完成。标准库没有一个,但NumPy有numpy.partition
的工作:
fork()
答案 5 :(得分:0)
在heapq中使用nsmallest数字的代码较少,但如果您希望自己实现它,这是一种简单的方法。这个解决方案只需要循环数据一次,但是由于他应用并且在O(log n)上运行,因此该算法在较小数量的k上表现最佳。
import heapq
def getsmallest(arr, k):
m = [-x for x in l[:k]]
heapq.heapify(m)
for num in arr[5:]:
print num, m
heapq.heappush(m, max(-num, heapq.heappop(m)))
return m
if __name__ == '__main__':
l = [1,2,3,52,2,3,1]
print getsmallest(l, 5)