算法思路:
最小堆可以维持堆顶元素为最小值。
应用:
- 求数组第k个大的数
Python代码:
1
2
3
4
5
6
7
8
9def min_heap(self, nums: List[int], k: int) -> List[int]:
if not nums:
return []
res = []
for i in range(len(nums)):
heappush(res, nums[i])
if len(res) > k:
heappop(res)
return res
max heap的话,入堆的数转负数,出堆后转为整数
1
2
3
4
5
6
7
8def max_heap(self, nums: List[int], k: int) -> List[int]:
res = []
for i in range(len(nums)):
heappush(res, -nums[i])
if len(res) > k:
heappop(res)
res = [-n for n in res]
return res
旧法不推荐
1
2
3
4
5
6
7
8
9
10from heapq import heapreplace, heappush
def min_heap(self, nums: List[int], k: int) -> List[int]:
res = []
for i in range(len(nums)):
if i < k:
heappush(res, nums[i])
elif nums[i] > res[0]:
heapreplace(res, nums[i])
return res
max heap的话,入堆的数转负数,跟堆顶比较的大于号不变,出堆后转为整数
注意第6行res[0]并没有负号,因为res已经是负数
1
2
3
4
5
6
7
8
9def max_heap(self, nums: List[int], k: int) -> List[int]:
res = []
for i in range(len(nums)):
if i < k:
heappush(res, -nums[i])
elif -nums[i] > res[0]:
heapreplace(res, -nums[i])
res = [-n for n in res]
return res
BFS + Heap
本质是图,求点或点的和或线段路径的最值
这是单源最短路径Dijkstra的典型应用。Dijkstra是每条边的权重(距离), 而不是节点个数最短(BFS模板)。
区别是
- 用BFS distance模板。queue变成heap每个点总weight的最小堆
- 遍历时候每个元素多一个weight
- visited不再是记录这个节点访问过没有,因为节点可以多次访问,目标是找到这个节点权重最小的路径。visited记录每个节点的最小权重(路径). visited的处理在neighbor的for循环中,比较权重是否最小,如果不是就跳过。
BFS的distance模板(用于对比加强对BFS+Heap模板的理解)
1
2
3
4
5
6
7
8
9
10
11def bfs(self, graph, start, target) -> List[int]:
heap = [(0, k)] # weight, node
visited = {k: 0}
while heap:
weight, node = heapq.heappop(heap)
for neighbor, _weight in graph[node]:
if neighbor in visited and visited[neighbor] <= weight + _weight:
continue
heapq.heappush(heap, (weight + _weight, neighbor))
visited[neighbor] = weight + _weight
return max(visited.values()) if len(visited) == n else -11
2
3
4
5
6
7
8
9
10
11
12
13def bfs_layer_v2(self, graph, start, target) -> int:
queue = deque([(start, 1)])
visited = {start}
while queue:
node, distance = queue.popleft()
if node == target:
return distance
for neighbor in graph[node]:
if neighbor in visited:
continue
queue.append((neighbor, distance + 1))
visited.add(neighbor)
return -1
算法分析:
时间复杂度为O(nlogk),空间复杂度O(1)。
例子:
LeetCode 743 Network Delay Time
求从某一个点出发的所有能到达的点中的最短时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
# build graph
graph = collections.defaultdict(list)
for e in times:
graph[e[0]].append((e[1], e[2]))
heap = [(0, k)] # weight, node
visited = {k: 0}
while heap:
weight, node = heapq.heappop(heap)
for neighbor, _weight in graph[node]:
if neighbor in visited and visited[neighbor] <= weight + _weight:
continue
heapq.heappush(heap, (weight + _weight, neighbor))
visited[neighbor] = weight + _weight
return max(visited.values()) if len(visited) == n else -1
LeetCode 787 Cheapest Flights Within K Stops
求只允许停k个站情况下,最便宜机票价格
weight在这里是每条边的价格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
graph = collections.defaultdict(list)
for pair in flights:
graph[pair[0]].append((pair[1], pair[2]))
heap = ([(0, src, 0)]) # price, node_id, distance
visited = {(src, 0): 0} # (node, distance): price
while heap:
p, node, dist = heapq.heappop(heap)
if node == dst:
return p
if dist == k + 1:
continue
for neighbor, _price in graph[node]:
if (neighbor, dist + 1) in visited and visited[(neighbor, dist + 1)] <= p + _price:
continue
heapq.heappush(heap, (p + _price, neighbor, dist + 1))
visited[(neighbor, dist + 1)] = p + _price
return -1
更简洁的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
graph = collections.defaultdict(list)
for pair in flights:
graph[pair[0]].append((pair[1], pair[2]))
heap = ([(0, src, 0)]) # price, node_id, distance
visited = {}
while heap:
p, node, dist = heapq.heappop(heap)
if node == dst and dist <= k + 1:
return p
if node in visited and dist >= visited[node]:
continue
visited[node] = dist
for neighbor, _price in graph[node]:
heapq.heappush(heap, (p + _price, neighbor, dist + 1))
return -1


