leetcode
leetcode 2651 ~ 2700
概率最大的路径

概率最大的路径

难度:

标签:

题目描述

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.

 

Example 1:

Input: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
Output: 0.25000
Explanation: 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.

Example 2:

Input: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
Output: 0.30000

Example 3:

Input: n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
Output: 0.00000
Explanation: There is no path between 0 and 2.

 

Constraints:

  • 2 <= n <= 10^4
  • 0 <= start, end < n
  • start != end
  • 0 <= a, b < n
  • a != b
  • 0 <= succProb.length == edges.length <= 2*10^4
  • 0 <= succProb[i] <= 1
  • There is at most one edge between every two nodes.

代码结果

运行时间: 121 ms, 内存: 26.2 MB


/* 
 * 思路:使用Dijkstra算法变种,将成功概率最大的路径视为距离最短路径。
 * 使用优先队列来维护当前最大概率的节点,并不断更新其邻接点的概率值。
 * 使用Java Stream API进行流式处理。
 */
import java.util.*;
import java.util.stream.*;

public class SolutionStream {
    public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
        Map<Integer, List<Node>> graph = IntStream.range(0, n)
            .boxed()
            .collect(Collectors.toMap(i -> i, i -> new ArrayList<>()));
        IntStream.range(0, edges.length).forEach(i -> {
            graph.get(edges[i][0]).add(new Node(edges[i][1], succProb[i]));
            graph.get(edges[i][1]).add(new Node(edges[i][0], succProb[i]));
        });
        double[] prob = new double[n];
        prob[start] = 1.0;
        PriorityQueue<Node> pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob));
        pq.offer(new Node(start, 1.0));
        while (!pq.isEmpty()) {
            Node current = pq.poll();
            int curNode = current.node;
            double curProb = current.prob;
            if (curNode == end) {
                return curProb;
            }
            graph.get(curNode).stream()
                .forEach(neighbor -> {
                    double newProb = curProb * neighbor.prob;
                    if (newProb > prob[neighbor.node]) {
                        prob[neighbor.node] = newProb;
                        pq.offer(new Node(neighbor.node, newProb));
                    }
                });
        }
        return 0.0;
    }

    class Node {
        int node;
        double prob;
        Node(int node, double prob) {
            this.node = node;
            this.prob = prob;
        }
    }
}

解释

方法:

这道题使用了图的最短路径算法的变种来寻找最大成功概率的路径。它使用了优先队列(堆)来实现一个类似于Dijkstra算法的过程,但是由于我们寻找的是最大概率而不是最短距离,我们在堆中存储的是概率的负值,以便最大概率的路径可以优先被处理。算法首先建立了一个图的邻接表表示,然后使用优先队列迭代地选择当前最大概率路径直到到达终点或队列为空,最后如果到达终点则返回当前的概率,否则返回0。

时间复杂度:

O(E log E)

空间复杂度:

O(N + E)

代码细节讲解

🦆
为什么在实现算法时选择使用优先队列(堆)而不是其他数据结构如队列或栈?
在求解最大概率路径的问题中,使用优先队列(堆)而不是队列或栈的主要原因是优先队列能够保证每次从队列中取出的是当前未处理节点中概率最大的节点。这是类似于Dijkstra算法的工作原理,它适用于处理带权重的图的最短路径问题,在这种场景下,权重是节点间的概率。优先队列的这种属性使得算法能够更加高效地向目标节点逼近,而不必遍历所有可能的路径。使用普通队列的广度优先搜索(BFS)或使用栈的深度优先搜索(DFS)虽然也能够遍历图,但在找到最大概率路径方面效率较低,因为它们无法保证每次处理的都是最有希望的节点。
🦆
在优先队列中使用概率的负值是基于什么考虑?这种表示方式有哪些优势?
在优先队列中使用概率的负值的主要考虑是Python中的优先队列(使用heapq实现)默认是最小堆,这意味着它总是先出队列中最小的元素。为了使算法能够优先处理最大概率的路径,我们通过存储概率的负值来逆转这一默认行为,使得数值上最小的(即最大的负数)对应于原始概率中数值上最大的。这种表示方式的优势在于,我们可以利用现有的最小堆数据结构来模拟最大堆的行为,从而简化代码实现并提高算法的效率。
🦆
算法中如何处理图中存在的多条边连接同一对节点的情况?是否考虑了概率的合并或选择?
题解中的算法似乎没有明确处理图中存在多条边连接同一对节点的情况,即可能未直接合并或选择概率。在这种情况下,邻接表可能会包含多个条目来表示同一对节点之间的不同概率的边。算法在处理时会考虑这些边作为独立的路径来处理。在实际应用中,如果需要优化,可以在构建图的邻接表时合并或选择最大的概率,只保留每对节点之间概率最大的边,这样可以简化图的结构并可能提高算法的效率。

相关问题