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
nnodes (0-indexed), represented by an edge list whereedges[i] = [a, b]is an undirected edge connecting the nodesaandbwith a probability of success of traversing that edgesuccProb[i].Given two nodes
startandend, find the path with the maximum probability of success to go fromstarttoendand return its success probability.
Some questions to ask
- Directed or undirected graph?
- Any parallel arcs?
- Guaranteed to have a path between
startandend?
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
startnode - Define its neighbours (and their probabilities) as
fringe - While
fringeis not empty:- Pick the node in
fringewith max probability fromstart - Either:
- The node is
end, which solves the problem; or - The node is not
end, so- We expand
fringewith 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
fringeis empty, that meansendcannot 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,visitedandneighbours