Java中的 Dijkstra 算法

Java中的 Dijkstra

应用场景

用于求单源最短路径(非负权图),其贪心策略是:"每次从未确定最短路的点中,选出当前距离起点最近的点,用它去更新邻居。"我们使用邻接表去储存图,一般都是有向图,只需要进行一次添加就可以,每个节点我们使用数组 int\[\] 存储,也可以自定义一个类去存储。

优先队列(PriorityQueue)

这里涉及到数据结构中的一个概念------堆,堆有最小堆和最大堆,我们 Dijkstra 算法使用的是最小堆,而优先队列创建的是最大堆,这时候我们就要做一些处理。

  • 原始创建------最大堆:
    PriorityQueue <Integer> PQ = new PriorityQueue<>();
  • 变式创建------最小堆:
    PriorityQueue <Integer> PQ = new PriorityQueue<>(Collections.reverseOrder());(仅用于最大变最小)
    或者 PriorityQueue <Integer> PQ = new PriorityQueue<>((a,b)->Integer.compare(a,b));
    或者PriorityQueue <Integer> PQ = new PriorityQueue<>(Integer::compareTo);
  • 常用函数(和队列是一样的)
    PQ.offer():入队
    PQ.poll():出队
    PQ.peek():查看
    PQ.isEmpty():检查是否为空
    唯一区别是优先队列没有remove(删除)

整数快读函数

代码实现(之前的基础快读next()函数因为新建了很多字符串用于中转,所以内存会爆,可以先试)

java 复制代码
 static int readInt(BufferedReader bf)throws IOException{
        int c,n =0;
        boolean neg = false;//接收该数字是否为负,如果确保是非负的数据,可以省略
        while((c = bf.read())<=' ')//制表符,换行符,空格之类的分割符的ASC码都小于空格
            if(c == -1)//-1代表数据结束
                return -1;
        if(c=='-'){//如果是负数
            neg = true;
            c = bf.read();
        }
        do{
            n = n*10+c-'0';
        }while((c = bf.read())>' ');
            return neg ? -n:n;//返回正数或者负数
    }

例题(FROM 洛谷 P3371)

思路

1.创建一个dist数组存储起点到该点的最短距离,其实有点像dp了,然后不断更新这个最短距离,最后输出

2.其实这个题除了整数快读函数还有一个解决办法,就是将输出用StringBuilder存储,只调用一次print函数,因为print函数也会导致缓存堆积,最终内存超限。(蒽。。。我刚才去试了一下,改输出的方法还是会有三个MLE)

代码实现

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

public class Main{
    static final int INT =  Integer.MAX_VALUE;
    static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    public static void main(String [] args) throws IOException{
        int n = readInt(bf);
        int m = readInt(bf);
        int s = readInt(bf);
        int [] dist = new int[n+1];
        Arrays.fill(dist,INT);
        dist[s] = 0;
        List <List<int[]>>list = new ArrayList<>(n+1);
        for(int i=0;i<=n;i++)
            list.add(new ArrayList<>());
        for(int i=0;i<m;i++){
            int u = readInt(bf);
            int v = readInt(bf);
            int w = readInt(bf);
            list.get(u).add(new int[]{v,w});
        }
        //int [] = {dist,node};
        PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)->Integer.compare(a[0],b[0]));//min_PQ
        pq.offer(new int [] {0,s});
        while(!pq.isEmpty()){
            int [] cur = pq.poll();
            int d = cur[0];
            int cu = cur[1];
            if(d!=dist[cu]) continue;
            for(int [] edge:list.get(cu)){
                int v = edge[0];
                int w = edge[1];
                int newdist = d+w;
                if(newdist<dist[v]){
                    dist[v] = newdist;
                    pq.offer(new int []{newdist,v});
                }
            }
        }
        for(int i=1;i<=n;i++)
            System.out.print(dist[i]+" ");
    }
    static int readInt(BufferedReader bf)throws IOException{
        int c,n =0;
        boolean neg = false;
        while((c = bf.read())<=' ')
            if(c == -1)
                return -1;
        if(c=='-'){
            neg = true;
            c = bf.read();
        }
        do{
            n = n*10+c-'0';
        }while((c = bf.read())>' ');
            return neg ? -n:n;
    }
}

注意:即使题里没说也要将dist全部初始化为 Integer.MAX_VALUE,否则会导致输出全为0------没有比0更小的了。
进阶训练:洛谷 P4779(这个不用整数快读,直接快读就能过,其他都一样)

例题2(FROM 洛谷 P1144)

该题融合了 dp 和 Dijkstra 和 BFS

代码实现

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

public class Main{
    static int MOD = 100003;
    static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    public static void main(String [] args)throws IOException{
        int n = Integer.parseInt(next()); //node 节点
        int m = Integer.parseInt(next());//edge 边
        List<List<Integer>> list = new ArrayList<>(n+1);
        for(int i=0;i<=n;i++)
            list.add(new ArrayList<>());
        for(int i=0;i<m;i++){
            int x = Integer.parseInt(next());
            int y = Integer.parseInt(next());
            if(x==y)//自环
                continue;
            else{
                list.get(x).add(y);
                list.get(y).add(x);
            }
        }
        int [] dp = new int [n+1];
        //dp[i] 表示顶点1到顶点i有多少条不同的最短路
        int [] dist = new int [n+1];
        //dist[i] 表示起点到该点的最短距离
        Arrays.fill(dist,-1);
        //dist [i] = -1 代表未达
        dist[1] = 0;
        dp[1] = 1;
        
        Queue<Integer> q = new ArrayDeque<>();
        q.offer(1);//第一个节点
        
        while(!q.isEmpty()){
            int u = q.poll();
            for(int v : list.get(u)){//遍历该节点的邻居
                if(dist[v] == -1){
                    dist[v] = dist[u] + 1;
                    dp[v] = dp[u];
                    q.offer(v);
                }
                else if(dist[v] == dist[u] + 1)
                //如果该点的邻居等于该点离起点的最小距离再走一步,那该邻居也是最小距离
                    dp[v] = (dp[v] + dp[u])% MOD;
            }
        }
        StringBuilder sb = new StringBuilder();//如果不用这个的话,会有一个TLE
        for(int i=1;i<=n;i++){
            sb.append(dp[i]);
            sb.append("\n");
        }
        System.out.print(sb);
    }
    static String next() throws IOException{
        while(st== null||!st.hasMoreTokens()){
            String str = bf.readLine();
            if(str == null) return null;
            st = new StringTokenizer (str);
        }
        return st.nextToken();
    }
}
相关推荐
8Qi87 小时前
回文子串(Palindromic Substrings)—— 题解
算法·leetcode·职场和发展·动态规划
二月夜9 小时前
剖析Java正则表达式回溯问题
java·正则表达式
xuhaoyu_cpp_java10 小时前
项目学习(三)分页查询
java·经验分享·笔记·学习
程序员二叉10 小时前
【Java】集合面试全套精讲|HashMap/ArrayList高频考点完整版
java·面试·哈希算法
cfm_291411 小时前
JVM GC垃圾回收初步了解
java·开发语言·jvm
心之伊始11 小时前
LangChain4j RAG 实战:Java 后端如何把本地文档接入 Embedding 检索链路
java·架构·源码分析·csdn
许彰午11 小时前
17_synchronized关键字深度解析
java·开发语言
小宋加油啊12 小时前
机械臂抓取物体 PVN3D算法调研学习
学习·算法·3d
lqqjuly12 小时前
前沿算法深度解析(一)
算法