03 Nov 2020
Path with maximum probability
Source: https://leetcode.com/problems/path-with-maximum-probabilityTags: graph, shortest path
Problem
You are given an undirected weighted graph of
n
nodes (0-indexed), represented by an edge list whereedges[i] = [a, b]
is an undirected edge connecting the nodesa
andb
with a probability of success of traversing that edgesuccProb[i]
.Given two nodes
start
andend
, find the path with the maximum probability of success to go fromstart
toend
and return its success probability.
Some questions to ask
- Directed or undirected graph?
- Any parallel arcs?
- Guaranteed to have a path between
start
andend
?
Approach
Implement Dijkstra’s algorithm for finding the shortest path between nodes, but adapt the metric of “shortest path” to look for “maximum probability” instead.
Basic idea:
- Begin at
start
node - Define its neighbours (and their probabilities) as
fringe
- While
fringe
is not empty:- Pick the node in
fringe
with max probability fromstart
- Either:
- The node is
end
, which solves the problem; or - The node is not
end
, so- We expand
fringe
with the neighbours of the current node. - For each neighbour:
- If prob(
start
->node
->neighbour
) > prob(start
->neighbour
), we use the larger probability in the fringe.
- If prob(
- We expand
- The node is
- Pick the node in
- If
fringe
is empty, that meansend
cannot be reached from start.
def maxProbability(n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
"""Return maximum probability from node `start` to node `end`."""
probFromStart = [int(node == start) for node in range(n)]
# Keep mapping from node to list of neighbours and path probabilities
neighbours = defaultdict(list)
for (src, dst), prob in zip(edges, succProb):
neighbours[src].append((dst, prob))
neighbours[dst].append((src, prob))
visited = set()
# Keep priority queue of nodes, with the priority being the
# *negated* probability from start node. We negate because
# Python's heap data structure implements a minheap.
fringe = [(-1, start)]
while fringe:
negProbAcc, node = heapq.heappop(fringe)
probToNode = -negProbAcc
if node == end:
return probToNode
visited.add(node)
for neighbour, nodeToNeighbour in neighbours[node]:
if neighbour in visited or nodeToNeighbour == 0:
continue
viaNeighbour = probToNode * nodeToNeighbour
if viaNeighbour > probFromStart[neighbour]:
# More probable path found
probFromStart[neighbour] = viaNeighbour
heapq.heappush(fringe, (-viaNeighbour, neighbour))
# No path found
return 0
Complexity
Let the number of edges be E.
- O((n + E)log(n)) time complexity, by implementing Dijkstra’s algorithm with (min-heap) priority queue
- O(n + E) space complexity, for keeping track of
probFromStart
,visited
andneighbours