Java中的 Dijkstra
- 应用场景
- 优先队列(PriorityQueue)
- 整数快读函数
- [例题(FROM 洛谷 P3371)](#例题(FROM 洛谷 P3371))
- [例题2(FROM 洛谷 P1144)](#例题2(FROM 洛谷 P1144))
应用场景
用于求单源最短路径(非负权图),其贪心策略是:"每次从未确定最短路的点中,选出当前距离起点最近的点,用它去更新邻居。"我们使用邻接表去储存图,一般都是有向图,只需要进行一次添加就可以,每个节点我们使用数组 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();
}
}