Skip to content

Commit 259556b

Browse files
committed
topk 注释
1 parent c8af01f commit 259556b

File tree

1 file changed

+16
-5
lines changed

1 file changed

+16
-5
lines changed

docs/15_堆与堆排序/heap_and_heapsort.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,25 @@ def test_heapsort_reverse():
123123

124124
# Python 里的 heapq 模块
125125
python 其实自带了 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
136147
import 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

177188
if __name__ == '__main__':

0 commit comments

Comments
 (0)