题目描述
思路
方法1 - BFS
- 关键点是: 当我们遍历到一个节点时,如果节点没有遍历过,那么可以继续遍历;如果遍历过节点,但是上次遍历时的概率比当先小,这个时候是需要重复遍历的(其实是贪心算法)
class Solution:
def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
if not edges or not edges[0]: return 0
# 构造节点邻接表
st_maps = collections.defaultdict(list)
for i, (s, e) in enumerate(edges):
st_maps[s].append((e, succProb[i]))
st_maps[e].append((s, succProb[i]))
ans = 0
queue = collections.deque([(start, 1)])
visited = {
start: 0} # value为已经遍历到该节点最大的概率值(还可能存在其他路径到该节点的概率更大的情况)
while queue:
# 当前节点
cur_node, cur_prob = queue.popleft()
for next_node, p in st_maps[cur_node]:
# 下一个待遍历的节点
next_prob = cur_prob * p
if next_node == end:
ans = max(ans, next_prob)
continue
# 剪枝和去重:如果下一个待遍历节点的概率大于ans && (该节点为遍历过 或 遍历过该节点但是上次的概率比现在小)
if next_prob > ans and (next_node not in visited or visited[next_node] < next_prob):
visited[next_node] = next_prob
queue.append((next_node, next_prob))
return ans
法2 - Dijkstra+优先队列
class Solution:
def maxProbability(self, n: int, edges, succProb: List[float], start: int, end: int) -> float:
# 用于标记节点是否已经访问过
vis = [0] * n
# 用于标记已经访问过的节点个数
cnt = 0
import heapq
# 最大堆,用于dijkstra里每次寻找当前未访问的节点中最大概率的节点
# 如果用普通队列,会超时,因为查找最大的时间复杂度是O(n)
q = []
heapq.heappush(q, (-1, start))
# 生成graph,数据格式为: {node1 : [(node2, prob_1_2), (node3, prob_1_3)], node2: [(node1: prob_1_2), (node3: prob_2_3)]}
graph = {
}
for i, item in enumerate(edges):
s, e, p = item[0], item[1], succProb[i]
graph.setdefault(s, []).append((e, p))
graph.setdefault(e, []).append((s, p))
# 循环查找每个节点
while cnt < n:
# 队列为空,还没有找到,说明没有路径,即end 和 start 未连通
if not q:
return 0
# 从最大堆中获取当前还没遍历过的节点里,概率最大的节点
maxProb, maxIdx = heapq.heappop(q)
while vis[maxIdx]:
maxProb, maxIdx = heapq.heappop(q)
# 如果maxIdx不在队列中 - 说明 start是孤立节点
if maxIdx not in graph:
return 0
# 找到终点,结束循环
if maxIdx == end:
return -maxProb
# 将该节点设置为已访问状态
vis[maxIdx] = True
cnt += 1
# 对于所有邻居节点,如果没有被访问过,放入队列
for e in graph[maxIdx]:
if not vis[e[0]]:
heapq.heappush(q, (maxProb * e[1], e[0]))
return 0