1
1
# 优先级队列 (堆)
2
2
3
+ 堆是一种二叉树,最小堆的父亲总是小于等于两个孩子。最大堆反之。因此,堆顶必为最值。
4
+
3
5
用到优先级队列 (priority queue) 或堆 (heap) 的题一般需要维护一个动态更新的池,元素会被频繁加入到池中或从池中被取走,每次取走的元素为池中优先级最高的元素 (可以简单理解为最大或者最小)。用堆来实现优先级队列是效率非常高的方法,加入或取出都只需要 O(log N) 的复杂度。
4
6
5
7
## Kth largest/smallest
6
8
7
- 找数据中第 K 个最大/最小数据是堆的一个典型应用。以找最大为例,遍历数据时,使用一个最小堆来维护当前最大的 K 个数据,堆顶数据为这 K 个数据中最小,即是你想要的第 k 个最大数据。每检查一个新数据,判断是否大于堆顶,若大于,说明堆顶数据小于了 K 个值,不是我们想找的第 K 个最大,则将新数据 push 进堆并 pop 掉堆顶,若小于则不操作,这样当遍历完全部数据后堆顶即为想要的结果。找最小时换成最大堆即可。
9
+ 找数据中第 K 个最大/最小数据是堆的一个典型应用。
10
+
11
+ 以找最大为例,遍历数据时,使用一个最小堆来维护当前最大的 K 个数据,堆顶数据为这 K 个数据中最小,即是你想要的第 k 个最大数据。每检查一个新数据,判断是否大于堆顶,若大于,说明堆顶数据小于了 K 个值,不是我们想找的第 K 个最大,则将新数据 push 进堆并 pop 掉堆顶,若小于则不操作,这样当遍历完全部数据后堆顶即为想要的结果。找最小时换成最大堆即可。
12
+
13
+ 注意,找第k个最大,居然是用最小堆。最小堆/最大堆只表示堆顶是堆内的最小/最大,用哪个看用途!【比如:需要多少个比它大的。】
8
14
9
15
### [ kth-largest-element-in-a-stream] ( https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/ )
10
16
14
20
class KthLargest :
15
21
16
22
def __init__ (self , k : int , nums : List[int ]):
17
- self .K = k
23
+ self .k = k
18
24
self .min_heap = []
19
25
for num in nums:
20
- if len (self .min_heap) < self .K :
26
+ if len (self .min_heap) < self .k :
21
27
heapq.heappush(self .min_heap, num)
22
28
elif num > self .min_heap[0 ]:
23
- heapq.heappushpop(self .min_heap, num)
29
+ _ = heapq.heappushpop(self .min_heap, num)
24
30
25
31
def add (self , val : int ) -> int :
26
- if len (self .min_heap) < self .K:
32
+ if len (self .min_heap) < self .k: # 就算题目保证查找第k大时至少有k个,也有可能初始化空堆,那样就不能self.min_heap[0]了!
27
33
heapq.heappush(self .min_heap, val)
28
34
elif val > self .min_heap[0 ]:
29
- heapq.heappushpop(self .min_heap, val)
30
-
35
+ _ = heapq.heappushpop(self .min_heap, val)
31
36
return self .min_heap[0 ]
32
37
```
33
38
@@ -37,27 +42,24 @@ class KthLargest:
37
42
38
43
- 此题使用 heap 来做并不是最优做法,相当于 N 个 sorted list 里找第 k 个最小,列有序的条件没有充分利用,但是却是比较容易想且比较通用的做法。
39
44
45
+ 具体见代码注释,大思路是维护一个"最小值候选堆"。弹出前k-1个最小值,堆顶就是第k小。【这里居然仍然是最小堆!interesting。记住。】
46
+
40
47
``` Python
41
48
class Solution :
42
49
def kthSmallest (self , matrix : List[List[int ]], k : int ) -> int :
50
+ n = len (matrix) # 矩阵是n*n的
51
+
52
+ pq = [(matrix[i][0 ], i, 0 ) for i in range (min (k, n))] # 取出第一列候选人
53
+ # matrix[i][0]是具体的值,后面的(i,0)是在记录候选人在矩阵中的位置,方便每次右移添加下一个候选人
54
+
55
+ heapq.heapify(pq) # 用下heapify API
56
+
57
+ for i in range (k - 1 ): # 弹出前k-1个最小数,堆顶就剩第k小
58
+ _, x, y = heapq.heappop(pq) # 弹出候选人里最小一个
59
+ if y != n - 1 : # 如果这一行还没被弹完,若弹完了就不候选这行了
60
+ heapq.heappush(pq, (matrix[x][y + 1 ], x, y + 1 )) # 加入这一行的下一个候选人
43
61
44
- N = len (matrix)
45
-
46
- min_heap = []
47
- for i in range (min (k, N)): # 这里用了一点列有序的性质,第k个最小只可能在前k行中(k行以后的数至少大于了k个数)
48
- min_heap.append((matrix[i][0 ], i, 0 ))
49
-
50
- heapq.heapify(min_heap)
51
-
52
- while k > 0 :
53
- num, r, c = heapq.heappop(min_heap)
54
-
55
- if c < N - 1 :
56
- heapq.heappush(min_heap, (matrix[r][c + 1 ], r, c + 1 ))
57
-
58
- k -= 1
59
-
60
- return num
62
+ return pq[0 ][0 ]
61
63
```
62
64
63
65
### [ find-k-pairs-with-smallest-sums] ( https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/ )
0 commit comments