力扣labuladong——一刷day85

文章介绍了如何利用Dijkstra算法求解力扣平台上的两个问题:最小体力消耗路径(1631题)通过BFS遍历和优先级队列优化,以及概率最大的路径(1514题)通过构建图并利用动态规划策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言


Dijkstra 算法(一般音译成迪杰斯特拉算法)无非就是一个 BFS 算法的加强版,它们都是从二叉树的层序遍历衍生出来的。

一、力扣1631. 最小体力消耗路径

class Solution {
    public int minimumEffortPath(int[][] heights) {
        int m = heights.length, n = heights[0].length;
        // 定义:从 (0, 0) 到 (i, j) 的最小体力消耗是 effortTo[i][j]
        int[][] effortTo = new int[m][n];
        // dp table 初始化为正无穷
        for (int i = 0; i < m; i++) {
            Arrays.fill(effortTo[i], Integer.MAX_VALUE);
        }
        // base case,起点到起点的最小消耗就是 0
        effortTo[0][0] = 0;

        // 优先级队列,effortFromStart 较小的排在前面
        Queue<State> pq = new PriorityQueue<>((a, b) -> {
            return a.effortFromStart - b.effortFromStart;
        });

        // 从起点 (0, 0) 开始进行 BFS
        pq.offer(new State(0, 0, 0));

        while (!pq.isEmpty()) {
            State curState = pq.poll();
            int curX = curState.x;
            int curY = curState.y;
            int curEffortFromStart = curState.effortFromStart;

            // 到达终点提前结束
            if (curX == m - 1 && curY == n - 1) {
                return curEffortFromStart;
            }

            if (curEffortFromStart > effortTo[curX][curY]) {
                continue;
            }
            // 将 (curX, curY) 的相邻坐标装入队列
            for (int[] neighbor : adj(heights, curX, curY)) {
                int nextX = neighbor[0];
                int nextY = neighbor[1];
                // 计算从 (curX, curY) 达到 (nextX, nextY) 的消耗
                int effortToNextNode = Math.max(
                        effortTo[curX][curY],
                        Math.abs(heights[curX][curY] - heights[nextX][nextY])
                );
                // 更新 dp table
                if (effortTo[nextX][nextY] > effortToNextNode) {
                    effortTo[nextX][nextY] = effortToNextNode;
                    pq.offer(new State(nextX, nextY, effortToNextNode));
                }
            }
        }
        // 正常情况不会达到这个 return
        return -1;
    }

    // 方向数组,上下左右的坐标偏移量
    int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    // 返回坐标 (x, y) 的上下左右相邻坐标
    List<int[]> adj(int[][] matrix, int x, int y) {
        int m = matrix.length, n = matrix[0].length;
        // 存储相邻节点
        List<int[]> neighbors = new ArrayList<>();
        for (int[] dir : dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            if (nx >= m || nx < 0 || ny >= n || ny < 0) {
                // 索引越界
                continue;
            }
            neighbors.add(new int[]{nx, ny});
        }
        return neighbors;
    }

    class State {
        // 矩阵中的一个位置
        int x, y;
        // 从起点 (0, 0) 到当前位置的最小体力消耗(距离)
        int effortFromStart;

        State(int x, int y, int effortFromStart) {
            this.x = x;
            this.y = y;
            this.effortFromStart = effortFromStart;
        }
    }
}

二、力扣1514. 概率最大的路径

class Solution {
    public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) {
        List<double[]>[] graph = builderGraph(edges, n, succProb);
        PriorityQueue<State> pq = new PriorityQueue<>(
            (a,b)->{
                return Double.compare(b.value, a.value);
            }
        );
        double[] flag = new double[n];
        Arrays.fill(flag,Integer.MIN_VALUE);
        flag[start_node] = 1;
        pq.offer(new State(start_node,1));
        while(!pq.isEmpty()){
            State cur = pq.poll();
            int curId = cur.id;
            double curValue = cur.value;
            if(curValue > flag[curId]){
                continue;
            }
            if(curId == end_node){
                return curValue;
            }
            for(double[] nexts : graph[curId]){
                int nextId = (int)nexts[1];
                double nextValue = nexts[2];
                if(nextValue * curValue > flag[nextId]){
                    flag[nextId] = nextValue * curValue;
                    pq.offer(new State(nextId,flag[nextId]));
                }
            }
        }
        return 0.0;
    }
    List<double[]>[] builderGraph(int[][] edges, int n, double[] succProb){
        List<double[]>[] graph = new LinkedList[n];
        for(int i = 0; i < n; i ++){
            graph[i] = new LinkedList<double[]>();
        }
        for(int i = 0; i < edges.length; i ++){
            int from = edges[i][0];
            int to = edges[i][1];
            graph[from].add(new double[]{from, to, succProb[i]});
            graph[to].add(new double[]{to,from,succProb[i]});
        }
        return graph;
    }
    class State{
        int id;
        double value;
        public State(int id, double value){
            this.id = id;
            this.value = value;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乱世在摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值