Dijkstra实现

1. 题目描述:矩阵海拔行走 (Base Version)

题目内容

小红拿到了一个 n×mn \times mn×m 的二维矩阵,矩阵的每个元素代表该位置的海拔高度(正整数)。小红从左上角 (0,0)(0, 0)(0,0) 出发,目标是到达右下角 (n−1,m−1)(n-1, m-1)(n−1,m−1)。

她每一步可以向上下左右四个方向移动。移动的代价 定义为:当前格子海拔与目标格子海拔之差的绝对值

请问:小红到达终点所需的最少体力消耗是多少?

输入限制

  • n,m≤500n, m \le 500n,m≤500
  • 矩阵元素 ≤106\le 10^6≤106

2. Java 标准实现

这份代码包含了我们之前讨论的所有关键点:极大值初始化优先队列排序 以及出队校验(懒惰删除)

java 复制代码
import java.util.*;

public class DijkstraMatrix {

    /**
     * Dijkstra 核心算法
     * @param graph 输入的二维矩阵
     * @return 从左上角到右下角的最短路径长度
     */
    public int solve(int[][] graph) {
        if (graph == null || graph.length == 0) return 0;
        
        int n = graph.length;
        int m = graph[0].length;
        
        // 1. 距离矩阵初始化为无穷大
        int[][] dist = new int[n][m];
        for (int i = 0; i < n; i++) {
            Arrays.fill(dist[i], Integer.MAX_VALUE);
        }
        
        // 2. 优先队列:按当前总代价从小到大排序 [row, col, time]。
        //队列中存放的是消息,每当节点X的临近节点Y认为,从(0,0)经过Y到达X耗时更少时,就会更新dist矩阵,并发送一个关于到达X耗时的消息到pq中。
        //由于pq中可能同时存在多个针对X的消息,因此pq可能是历史某节点发送的,而dist为真正最后一个节点落地的
        //所以存在关系dist[x][y]<=pq.poll()[2]
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[2]));
        
        // 3. 起点初始化
        dist[0][0] = 0;
        pq.offer(new int[]{0, 0, 0});
        
        int[] dr = {0, 0, 1, -1};
        int[] dc = {1, -1, 0, 0};

        while (!pq.isEmpty()) {
            int[] cur = pq.poll();
            int x = cur[0];
            int y = cur[1];
            int d = cur[2];

            // 4. 真理时刻:校验出队的消息是否过时
            if (d > dist[x][y]) continue;
            
            // 5. 终点提前返回优化
            if (x == n - 1 && y == m - 1) return d;

            // 6. 探索四周
            for (int i = 0; i < 4; i++) {
                int nr = x + dr[i];
                int nc = y + dc[i];
                
                if (nr >= 0 && nr < n && nc >= 0 && nc < m) {
                    int cost = Math.abs(graph[nr][nc] - graph[x][y]);
                    // 当前节点发现经过自己到达某周围节点的距离更短,于是更新周围节点距离,并发送消息
                    if (d + cost < dist[nr][nc]) {
                        dist[nr][nc] = d + cost;
                        pq.offer(new int[]{nr, nc, dist[nr][nc]});
                    }
                }
            }
        }
        return dist[n - 1][m - 1];
    }
}

3. 测试脚本 (JUnit 思想实现)

这个脚本包含三个典型用例:基础路径、障碍绕行(高海拔墙)、以及平地移动。

java 复制代码
public class TestDijkstra {
    public static void main(String[] args) {
        DijkstraMatrix solver = new DijkstraMatrix();
        
        // --- 用例 1:基础 3x3 绕行 ---
        int[][] case1 = {
            {1, 10, 10},
            {2, 3, 10},
            {10, 4, 1}
        };
        // 路径:(0,0)->(1,0)->(1,1)->(2,1)->(2,2)
        // 代价:|1-2| + |2-3| + |3-4| + |4-1| = 1 + 1 + 1 + 3 = 6
        assertTest(1, solver.solve(case1), 6);

        // --- 用例 2:平原(代价为 0) ---
        int[][] case2 = {
            {5, 5, 5},
            {5, 5, 5}
        };
        assertTest(2, solver.solve(case2), 0);

        // --- 用例 3:大跨度落差 ---
        int[][] case3 = {
            {1, 100},
            {100, 1}
        };
        // 路径:(0,0)->(0,1)->(1,1) = 99 + 99 = 198
        // 路径:(0,0)->(1,0)->(1,1) = 99 + 99 = 198
        assertTest(3, solver.solve(case3), 198);
        
        System.out.println("\n🎉 所有测试用例通过!基础 Dijkstra 逻辑稳固。");
    }

    private static void assertTest(int id, int actual, int expected) {
        if (actual == expected) {
            System.out.println("Test Case " + id + ": Passed! (Result: " + actual + ")");
        } else {
            System.err.println("Test Case " + id + ": FAILED! (Expected: " + expected + ", but got: " + actual + ")");
            System.exit(1);
        }
    }
}

4. 深度复盘:你学到了什么?

  1. 初始化 :如果不把 dist 填满 Integer.MAX_VALUE,程序会因为 0 是最小值而无法更新任何点。
  2. PQ 比较器Comparator.comparingInt(a -> a[2]) 确保了"贪心"策略的正确性,先处理当前总代价最小的点。
  3. 懒惰删除if (d > dist[x][y]) continue; 解决了队列滞后性带来的冗余计算,是性能优化的关键。
  4. 无后效性 :因为绝对值差永远 ≥0\ge 0≥0,所以 Dijkstra 才能保证第一个出队的终点就是最优解。
相关推荐
木斯佳3 小时前
前端八股文面经大全: 蓝色光标前端一面OC(2026-03-23)·面经深度解析
前端·面试·vue·校招·js·面经
野犬寒鸦3 小时前
从零起步学习AI大模型应用开发 || 第三章:智能体项目实战中的问题与解决方案及思路详解
java·服务器·数据库·人工智能·后端·面试
小江的记录本4 小时前
【Java】Java核心关键字:final、static、volatile、synchronized、transient(附《面试高频考点》)
java·开发语言·spring boot·后端·sql·spring·面试
ShineWinsu4 小时前
对于Linux:基础开发工具(vim、gcc/g++)的介绍
linux·运维·服务器·c++·面试·编辑器·vim
我叫黑大帅4 小时前
Gin 实战入门:从环境搭建到企业级常用特性全解析
后端·面试·go
我叫黑大帅5 小时前
Gin 日志体系详解
后端·面试·go
Elieal5 小时前
java基础面试
java·开发语言·面试
员工入职背调5 小时前
设定“面试预检”降本增效准则:重塑大批量用工的风险前置逻辑
面试·职场和发展
六义义6 小时前
SpringBoot 超详细全解(入门 + 实战 + 原理 + 面试)
java·spring boot·面试