算法初探LeetCode-概率最大的路径

80 阅读2分钟

LeetCode1514:概率最大的路径

给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。

指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。

如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。

示例 1:

输入: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出: 0.25000
解释: 从起点到终点有两条路径,其中一条的成功概率为 0.2 ,而另一条为 0.5 * 0.5 = 0.25

示例 2:

输入: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
输出: 0.30000

示例 3:

输入: n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
输出: 0.00000
解释: 节点 0 和 节点 2 之间不存在路径

提示:

  • 2<=n<=1042 <= n <= 10^4
  • 0 <= start, end < n
  • start != end
  • 0 <= a, b < n
  • a != b
  • 0<=succProb.length==edges.length<=21040 <= succProb.length == edges.length <= 2*10^4
  • 0 <= succProb[i] <= 1
  • 每两个节点之间最多有一条边

思路分析

首先建图,将图转为邻接表表示,并且将概率作为该边的权重一同存入邻接表中。因为本题是无向图,所以在建图的时候记得双向存储信息。 建好图之后直接套Dijkstra框架,但是本题需要求起点到终点成功概率最大的路径,所以在最开始的时候起点到起点的概率应该是1。起点到其他节点的概率因为置为一个不可能达到的值。

使用邻接矩阵记录边与点的关系,再使用bfs广度遍历出起点到各个点的距离,松弛操作后得到最大的概率(dijkstra ),最后使用大顶堆优化

算法代码

class State {
    int node;
    double disFromStart;
    State(int node, double disFromStart) {
        this.node = node;
        this.disFromStart = disFromStart;
    }
}
public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
        List < double[] > [] graph = buildGraph(n, edges, succProb);
        double[] disTo = new double[n];
        Arrays.fill(disTo, -1);
        // 起点到起点的概率
        disTo[start] = 1.0;
        // 大顶堆
        PriorityQueue < State > pq = new PriorityQueue < > ((s1, s2) - > {
            return Double.compare(s2.disFromStart, s1.disFromStart);
        });
        // 加入起点
        pq.add(new State(start, 1.0));
        while (!pq.isEmpty()) {
            State curState = pq.poll();
            int curNode = curState.node;
            double curDis = curState.disFromStart;
            // 如果已经到了终点 因为是大顶堆所以直接返回
            if (curNode == end) return disTo[curNode];
            // 如果当前概率小于之前存的概率 直接下一个 (因为概率肯定是越乘越小)
            if (curDis < disTo[curNode]) continue;
            // 邻居
            for (double[] next: graph[curNode]) {
                int nextNode = (int)(next[0]);
                double nextpro = next[1];
                // 到达下一个节点的概率等于 
                // 到达当前节点的概率*当前节点到下一个节点的概率
                double nextDis = disTo[curNode] * nextpro;
                // 如果存的概率小于最新得到的概率 则更新
                if (disTo[nextNode] < nextDis) {
                    disTo[nextNode] = nextDis;
                    pq.add(new State(nextNode, nextDis));
                }
            }
        }
        return 0.0;
    }
    // 建图
public List < double[] > [] buildGraph(int n, int[][] edges, double[] succProb) {
    List < double[] > [] graph = new LinkedList[n];
    int m = edges.length;
    for (int i = 0; i < n; i++) {
        graph[i] = new LinkedList < > ();
    }
    for (int i = 0; i < m; i++) {
        int from = edges[i][0];
        int to = edges[i][1];
        double weight = succProb[i];
        // 因为是无向图 所以from-to to-from都需要存
        graph[from].add(new double[] {
            to * 1.0, weight
        });
        graph[to].add(new double[] {
            from * 1.0, weight
        });
    }
    return graph;
}

结果详情

nxy1.png

算法复杂度

  • 空间复杂度:O(1)O(1)
  • 时间复杂度:O(n)O(n)

掘金(JUEJIN)一起进步,一起成长!