@@ -123,14 +123,25 @@ def test_heapsort_reverse():
123
123
124
124
# Python 里的 heapq 模块
125
125
python 其实自带了 heapq 模块,用来实现堆的相关操作,原理是类似的。请你阅读相关文档并使用内置的 heapq 模块完成堆排序。
126
- 一般我们刷题或者写业务代码的时候,使用这个内置的 heapq 模块就够用了。
126
+ 一般我们刷题或者写业务代码的时候,使用这个内置的 heapq 模块就够用了,内置的实现了是最小堆 。
127
127
128
128
129
129
# Top K 问题
130
130
面试题中有这样一类问题,让求出大量数据中的top k 个元素,比如一亿个数字中最大的100个数字。
131
131
对于这种问题有很多种解法,比如直接排序、mapreduce、trie 树、分治法等,当然如果内存够用直接排序是最简单的。
132
132
如果内存不够用呢? 这里我们提一下使用固定大小的堆来解决这个问题的方式。
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
+ 按照这个思路很容易写出来代码:
134
145
135
146
``` py
136
147
import heapq
@@ -156,9 +167,9 @@ class TopK:
156
167
if val < min_val: # 当然你可以直接 if val > min_val操作,这里我只是显示指出跳过这个元素
157
168
pass
158
169
else :
159
- heapq.heapreplace(self .minheap, val)
170
+ heapq.heapreplace(self .minheap, val) # 返回并且pop堆顶最小值,推入新的 val 值并调整堆
160
171
else :
161
- heapq.heappush(self .minheap, val)
172
+ heapq.heappush(self .minheap, val) # 前面 k 个元素直接放入minheap
162
173
163
174
def get_topk (self ):
164
175
for val in self .iterable:
@@ -171,7 +182,7 @@ def test():
171
182
i = list (range (1000 )) # 这里可以是一个可迭代元素,节省内存
172
183
random.shuffle(i)
173
184
_ = TopK(i, 10 )
174
- print (_.get_topk())
185
+ print (_.get_topk()) # [990, 991, 992, 996, 994, 993, 997, 998, 999, 995]
175
186
176
187
177
188
if __name__ == ' __main__' :
0 commit comments