@@ -123,14 +123,25 @@ def test_heapsort_reverse():
123123
124124# Python 里的 heapq 模块
125125python 其实自带了 heapq 模块,用来实现堆的相关操作,原理是类似的。请你阅读相关文档并使用内置的 heapq 模块完成堆排序。
126- 一般我们刷题或者写业务代码的时候,使用这个内置的 heapq 模块就够用了。
126+ 一般我们刷题或者写业务代码的时候,使用这个内置的 heapq 模块就够用了,内置的实现了是最小堆 。
127127
128128
129129# Top K 问题
130130面试题中有这样一类问题,让求出大量数据中的top k 个元素,比如一亿个数字中最大的100个数字。
131131对于这种问题有很多种解法,比如直接排序、mapreduce、trie 树、分治法等,当然如果内存够用直接排序是最简单的。
132132如果内存不够用呢? 这里我们提一下使用固定大小的堆来解决这个问题的方式。
133- 其实思路比较简单,先迭代前 k 个元素建立一个最小堆,之后的元素如果小于堆顶最小值,跳过,否则替换堆顶元素。
133+
134+ 一开始的思路可能是,既然求最大的 k 个数,是不是应该维护一个包含 k 个元素的最大堆呢?
135+ 稍微尝试下你会发现走不通。我们先用数组的前面 k 个元素建立最大堆,然后对剩下的元素进行比对,但是最大堆只能每次获取堆顶
136+ 最大的一个元素,如果我们取下一个大于堆顶的值和堆顶替换,你会发现堆底部的小数一直不会被换掉。如果下一个元素小于堆顶
137+ 就替换也不对,这样可能最大的元素就被我们丢掉了。
138+
139+ 相反我们用最小堆呢?
140+ 先迭代前 k 个元素建立一个最小堆,之后的元素如果小于堆顶最小值,跳过,否则替换堆顶元素并重新调整堆。你会发现最小堆里
141+ 慢慢就被替换成了最大的那些值,并且最后堆顶是最大的 topk 个值中的最小值。
142+ (比如1000个数找10个,最后堆里剩余的是 [ 990, 991, 992, 996, 994, 993, 997, 998, 999, 995] ,第一个 990 最小)
143+
144+ 按照这个思路很容易写出来代码:
134145
135146``` py
136147import heapq
@@ -156,9 +167,9 @@ class TopK:
156167 if val < min_val: # 当然你可以直接 if val > min_val操作,这里我只是显示指出跳过这个元素
157168 pass
158169 else :
159- heapq.heapreplace(self .minheap, val)
170+ heapq.heapreplace(self .minheap, val) # 返回并且pop堆顶最小值,推入新的 val 值并调整堆
160171 else :
161- heapq.heappush(self .minheap, val)
172+ heapq.heappush(self .minheap, val) # 前面 k 个元素直接放入minheap
162173
163174 def get_topk (self ):
164175 for val in self .iterable:
@@ -171,7 +182,7 @@ def test():
171182 i = list (range (1000 )) # 这里可以是一个可迭代元素,节省内存
172183 random.shuffle(i)
173184 _ = TopK(i, 10 )
174- print (_.get_topk())
185+ print (_.get_topk()) # [990, 991, 992, 996, 994, 993, 997, 998, 999, 995]
175186
176187
177188if __name__ == ' __main__' :
0 commit comments