You are given an undirected weighted graph of n nodes (0-indexed), represented by an edge list where edges[i] = [a, b] is an undirected edge connecting the nodes a and b with a probability of success of traversing that edge succProb[i].
Given two nodes start and end, find the path with the maximum probability of success to go from start to end and return its success probability.
If there is no path from start to end, return 0. Your answer will be accepted if it differs from the correct answer by at most 1e-5.
Input: n =3, edges =[[0,1],[1,2],[0,2]], succProb =[0.5,0.5,0.2], start =0, end =2Output: 0.25000Explanation: There are two paths from start to end, one having a probability of success =0.2 and the other has 0.5*0.5=0.25.
Method 1 - Using Dijkstra Algorithm with PriorityQueue#
The approach in Java will be similar to the Python solution, using Dijkstra’s algorithm adapted for finding the max probability path. Here’s how you can implement it:
Graph Representation: Use an adjacency list.
Priority Queue: Use a priority queue to maintain nodes sorted by their maximum probability path.
Relaxation: Update probabilities and push the node to the queue if a higher probability path is found.
Termination Check: Return the probability once the end node is reached; otherwise, return 0 if no path exists.
import heapq
defmaxProbability(n, edges, succProb, start, end):
# Step 1: Create the graph graph = [[] for _ in range(n)]
for (a, b), prob in zip(edges, succProb):
graph[a].append((b, prob))
graph[b].append((a, prob))
# Step 2: Use a max-heap (in Python, we use a min-heap with negative probabilities) max_heap = [(-1.0, start)] # (-prob, node) max_prob = [0.0] * n
max_prob[start] =1.0while max_heap:
# Step 3: Get the node with the highest probability prob, node = heapq.heappop(max_heap)
prob =-prob
# Step 4: If we reached the end node, return the probabilityif node == end:
return prob
# Step 5: Relax edgesfor neighbor, edge_prob in graph[node]:
new_prob = prob * edge_prob
if new_prob > max_prob[neighbor]:
max_prob[neighbor] = new_prob
heapq.heappush(max_heap, (-new_prob, neighbor))
# If we exhaust the queue without finding the end nodereturn0.0
⏰ Time complexity: O(E log V), where E is number of edges, and V is number of nodes or vertices.
It takes O(E) time to build graph
Inserting 1 element in PQ takes O(log V) time, and each node can be inserted multiple times, which is proportional to E times, hence takes O((E)logV) times
🧺 Space complexity: O(E+V)
priority queue will store all nodes, and each node might be added more than once but at most O(V) elements will be in the queue at any time.
maxProb array takes O(V) space
Graph storage requires O(E+V) space
Method 2 - Using Dijkstra Algorithm closer to BFS#
classSolution {
staticclassProbabilityEdge {
int vertex;
double prob;
ProbabilityEdge(int vertex, double prob) {
this.vertex= vertex;
this.prob= prob;
}
}
publicdoublemaxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
Map<Integer, List<ProbabilityEdge>> gr =new HashMap<>();
for (int i = 0; i < edges.length; ++i) {
int[] edge = edges[i];
int u = edge[0];
int v = edge[1];
double prob = succProb[i];
gr.putIfAbsent(u, new ArrayList<>());
gr.putIfAbsent(v, new ArrayList<>());
gr.get(u).add(new ProbabilityEdge(v, prob));
gr.get(v).add(new ProbabilityEdge(u, prob));
}
double[] probs =newdouble[n]; // best prob so far for each node Queue<ProbabilityEdge> queue =new LinkedList<>();
queue.add(new ProbabilityEdge(start, 1.0));
while (!queue.isEmpty()) {
ProbabilityEdge curr = queue.poll();
int parent = curr.vertex;
double prob = curr.prob;
for (ProbabilityEdge nei : gr.getOrDefault(parent, new ArrayList<>())) {
// add to queue only if: it can make a better probdouble newProb = prob * nei.prob;
if (probs[nei.vertex]>= newProb) {
continue;
}
queue.add(new ProbabilityEdge(nei.vertex, newProb));
probs[nei.vertex]= newProb;
}
}
return probs[end];
}
}