Python中关于堆的操作
注意, python默认的是最小堆
什么时候想到用堆
A: 流!或者我们只关心k个元素
373 查找和最小的前k对数字
给定两个以 非递减顺序排列 的整数数组 nums1 和 nums2 , 以及一个整数 k 。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。
请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
class Solution:
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
m = len(nums1)
n = len(nums2)
visited = set()
ans = []
h = []
heappush(h, (nums1[0] + nums2[0], 0, 0))
visited.add((0, 0))
while len(ans) < k:
num, i, j = heappop(h)
ans.append([nums1[i], nums2[j]])
if (i + 1, j) not in visited and i + 1 < m and j < n:
heappush(h, (nums1[i+1] + nums2[j], i+1, j))
visited.add((i+1, j))
if (i, j + 1) not in visited and i < m and j + 1 < n:
heappush(h, (nums1[i] + nums2[j + 1], i, j + 1))
visited.add((i, j+1))
return ans
后面的优化就是怎么样能不用hashset
换个角度,如果要把 (i,j) 入堆,那么之前出堆的下标对是什么?
根据上面的讨论,出堆的下标对只能是 (i−1,j) 和 (i,j−1)。
只要保证 (i−1,j) 和 (i,j−1) 的其中一个会将 (i,j) 入堆,而另一个什么也不做,就不会出现重复了!
不妨规定 (i,j−1) 出堆时,将 (i,j) 入堆;而 (i−1,j) 出堆时只计入答案,其它什么也不做。
换句话说,在 (i,j) 出堆时,只需将 (i,j+1) 入堆,无需将 (i+1,j) 入堆。
但若按照该规则,初始仅把 (0,0) 入堆的话,只会得到 (0,1),(0,2),⋯ 这些下标对。
所以初始不仅要把 (0,0) 入堆,(1,0),(2,0),⋯ 这些都要入堆。
class Solution:
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
ans = []
h = [(nums1[i] + nums2[0], i, 0) for i in range(min(len(nums1), k))]
while len(ans) < k:
_, i, j = heappop(h)
ans.append([nums1[i], nums2[j]])
if j + 1 < len(nums2):
heappush(h, (nums1[i] + nums2[j + 1], i, j + 1))
return ans
347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按任意顺序返回答案。
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
O ( n l o g n ) O(nlogn) O(nlogn)的算法显然很简单, 但有没有方法可以降一下这个呢? 事实上, 我们只关心前k个, 我们变能把复杂度降为 O ( n l o g k ) O(nlogk) O(nlogk), 那么能保持这个前k个元素的结构自然就是堆了.
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
frequencyMap = {}
for num in nums:
if num in frequencyMap:
frequencyMap[num] += 1
else:
frequencyMap[num] = 1
minHeap = []
for item, freq in frequencyMap.items():
print(minHeap)
if len(minHeap) < k:
heappush(minHeap, (freq, item))
else:
top_node = minHeap[0]
if freq > top_node[0]:
heappop(minHeap)
heappush(minHeap, (freq, item))
return [num for freq, num in minHeap]
23 合并k个升序链表
ListNode.__lt__ = lambda a, b: a.val < b.val # 让堆可以比较节点大小
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
cur = dummy = ListNode() # 哨兵节点,作为合并后链表头节点的前一个节点
h = []
for head in lists:
if head:
h.append(head)
heapify(h) # 堆化
while h: # 循环直到堆为空
node = heappop(h) # 剩余节点中的最小节点
if node.next: # 下一个节点不为空
heappush(h, node.next) # 下一个节点有可能是最小节点,入堆
cur.next = node # 合并到新链表中
cur = cur.next # 准备合并下一个节点
return dummy.next # 哨兵节点的下一个节点就是新链表的头节点