Dijkstra算法(朴素版&堆优化版)

一.写题选哪个版本:

我怎么知道题目是节点少边少、节点少边多、节点多边少、节点多边多呢

这个问题问得非常实际!能问出这个问题,说明你已经完全不满足于"背代码"了

我直接给你一套肉眼判断法,不用分析复杂度,扫一眼题目就能决定用哪个算法。


1、四种情况的判断标准(直接看输入范围)

题目一定会给出数据范围,这就是你的判断依据:

情况 V(节点数) E(边数) 特点 算法选择
节点少边少 ≤ 500 ≤ 5000 稀疏图 堆优化 Dijkstra / 朴素也行
节点少边多 ≤ 500 ≈ V²(完全图) 稠密图 朴素 Dijkstra(O(V²) 最快)
节点多边少 10⁴ ~ 10⁵ 10⁵ ~ 10⁶ 稀疏图 堆优化 Dijkstra(必须)
节点多边多 10⁴ ~ 10⁵ ≈ V²(几乎不可能出现) 稠密大图 基本不会出现(会爆内存)

2、具体题目对照

题目 V 范围 E 范围 类型 推荐算法
743 网络延迟 n ≤ 100 E ≤ 6000 节点少边多(稠密) 朴素 Dijkstra
1334 阈值距离 n ≤ 100 E 可能很多 节点少边多 朴素 Dijkstra
1631 最小体力 m×n ≤ 10⁴ ≈ 4V(网格图) 节点多边少 堆优化 Dijkstra
1584 连接点 n ≤ 1000 完全图(V²/2) 节点中等边极多 朴素 Prim(稠密)

3、更简单的判断口诀(面试够用)

V 小(≤ 500)→ 无脑朴素
V 大(> 1000)→ 无脑堆优化

为什么?

  • V 小的时候,O(V²) 最多 25 万,堆优化的常数反而吃亏

  • V 大的时候,O(V²) 会爆炸(1 亿+),必须用 O(E log V)

二.dijkstra朴素版

1.基础代码:

java 复制代码
package com.Dijkstra;

import java.util.Arrays;

public class Dijkstra {
    public static void main(String[] args) {
        int n=5;
        int INF=Integer.MAX_VALUE/2;
        int[][] graph={
                {0, 2, INF, 6, INF},
                {2, 0, 3, 8, 5},
                {INF, 3, 0, INF, 7},
                {6, 8, INF, 0, 9},
                {INF, 5, 7, 9, 0}
        };
        int start=0;
        int[] dist=dijkstra(n,graph,start);
        System.out.println("从0节点到其他所有节点的距离:");
        for (int i = 0; i < n; i++) {
            if(i==start) continue;
            System.out.println("0->"+i+": "+dist[i]);
        }
    }
    public static int[] dijkstra(int n,int[][] graph,int start){
        int[] dist=new int[n];
        boolean[] visited=new boolean[n];
        int[] parent=new int[n];
        //初始化
        Arrays.fill(dist,Integer.MAX_VALUE);
        Arrays.fill(parent,-1);
        dist[start]=0;
        for (int i = 0; i < n; i++) {
            int cur=-1;
            int min=Integer.MAX_VALUE;
            //找最小
            for (int j = 0; j < n; j++) {
                if(!visited[j]&&dist[j]<min){
                    min=dist[j];
                    cur=j;
                }
            }
            if(cur==-1) break;
            visited[cur]=true;
            //更新邻居路径
            for (int j = 0; j < n; j++) {
                if(!visited[j]){
                    int newDist=dist[cur]+graph[cur][j];
                    if(newDist<dist[j]){
                        dist[j]=newDist;
                        parent[j]=cur;
                    }
                }
            }
        }
        return dist;
    }
}

2.思路:

dist\[\]存的是七点到该节点的最短路径

visited\[\]是该节点是否激活(是否被访问过),true是激活,false是未激活

parent\[\]存的是得到该节点的最短路径的上一个节点,也就是父结点,看是谁使它更新的最短路径

先找最小节点并用cur记录,再更新邻居节点的最短路径

细节:

1.注意是有向图还是无向图,如果是无向图再更新邻居节点的时候就要分类讨论谁作为邻居和cur本身

2.newDist的计算是distcur+该两点之间的路径长度,进行newDist和dist邻居比较

三.dijkstra堆优化版

1.基础代码

java 复制代码
package com.Dijkstra;

import java.util.*;

public class DijkstraHeap {
    /**
     * Dijkstra 堆优化版(求从 start 到所有点的最短距离)
     * @param n 节点个数(编号 0 ~ n-1)
     * @param graph 邻接表,graph[u] = List<int[]{v, w}> 表示 u->v 边权 w
     * @param start 起点
     * @return dist 数组,dist[i] 表示 start 到 i 的最短距离
     */
    public static int[] dijkstra(int n, List<int[]>[] graph, int start) {
        // 1. 距离数组,初始化为无穷大
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[start] = 0;

        // 2. 优先队列(小顶堆),存 [节点, 当前最短距离]
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
        pq.offer(new int[]{start, 0});

        // 3. 主循环
        while (!pq.isEmpty()) {
            int[] cur = pq.poll();
            int u = cur[0];
            int d = cur[1];

            // 如果队列里的距离比已经记录的远,跳过(重要优化)
            if (d > dist[u]) continue;

            // 遍历所有邻居
            for (int[] edge : graph[u]) {
                int v = edge[0];
                int w = edge[1];
                int newDist = dist[u] + w;
                if (newDist < dist[v]) {
                    dist[v] = newDist;
                    pq.offer(new int[]{v, newDist});
                }
            }
        }
        return dist;
    }
}

2.思路

利用堆每次poll最小的节点,然后更新邻居节点的dist,在更新的同时要进行pq.offer( );

解释:if (d > distu) continue;

在操作的过程中会出现更新的情况,假如说节点2的现在的最短路径是dist2=5,然后在往下继续遍历的过程中节点2的最短路径现在是d=10, pq = {210, 28},然后根据pq的性质是升序排列的pq就会先弹出的是 28,再弹出 210,如果不加if (d > distu) continue;让 210直接退出,会造成它会向下错误的改变邻居节点的dist

举例:

修改一点图结构,加一个节点 3:

text

复制代码
0 → 1 (5)
0 → 2 (10)
1 → 2 (3)
2 → 3 (1)

目标还是 0 → 2(顺便看一下 3 会不会被错误更新)。

正确最短路径:
  • 0 → 1 → 2 → 3

    5 + 3 + 1 = 9

如果没有 if (d > dist[u]) continue

初始状态:

text

复制代码
dist = [0, ∞, ∞, ∞]
pq = [0[0]]

第 1 步:弹出 0,0

  • 更新 1,52,10

    pq = 1\[5, 210]

第 2 步:弹出 1,5

  • 更新 2,8

    pq = 2\[8, 210]

第 3 步:弹出 2,8

  • 更新 3,9

    pq = 2\[10, 39]

第 4 步弹出 2,10❌ 旧数据

  • 没有 continue 的话,会执行这一行:
    newDist = dist[2] + 1 = 10 + 1 = 11

  • 比较 11 < dist[3] (9) ❌ 不成立,不会更新

四.刷题

两种方法都可以写:

743. 网络延迟时间

1334. 阈值距离内邻居最少的城市

只能用堆优化版:

1631. 最小体力消耗路径(因为该题是 节点多n*n 10^4 边少)

1514. 概率最大的路径(节点多边少)

复习:01dx课程🙂

相关推荐
随意起个昵称3 小时前
区间dp-基础题目1(石子合并)
算法·动态规划
吞下星星的少年·-·3 小时前
线段树模板
算法
橙淮3 小时前
并发编程(六)
java·jvm
段一凡-华北理工大学3 小时前
2026 高炉炼铁智能化技术全景与演进路径~系列文章11:演进路径与行业未来
大数据·网络·人工智能·算法·工业智能体·高炉炼铁智能化
拽着尾巴的鱼儿3 小时前
springboot openfeign 自定义feign 接口重试机制
java·spring boot·后端
白露与泡影3 小时前
2026大厂Java面试题大全!牛客网最新版
java·开发语言
叶小鸡4 小时前
小鸡玩算法-力扣HOT100-多维动态规划
算法·leetcode·动态规划
星马梦缘4 小时前
aaaaa
数据结构·c++·算法
EntyIU4 小时前
JVM内存与GC笔记
java·jvm·笔记
OpenApi.cc4 小时前
神经网络结构驱动+数据结构分析
数据结构·人工智能·神经网络