Dijkstra 算法,解决 有向 非负权图 的单源最短路径算法。
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
/**
* @author wangshaoyu
*/
public class Main {
// 快速输入输出
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(br);
static int nextInt() throws IOException {
st.nextToken();
return (int)st.nval;
}
static ArrayList<Edge>[] adj; // 邻接表结构存储顶点的数组
static Edge[] edgeTo;
static int[] distTo;
static PriorityQueue<Pair> pq; // 存放 distTo[index] 的值
static boolean[] visited; // 用于标记到达某个点的的最短路径是否已经找到
public static void main(String[] args) throws IOException {
int n = nextInt(); // n 个顶点
int m = nextInt(); // m 条边
int start = nextInt() - 1; // 源点
// 建图
adj = new ArrayList[n];
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<>();
}
// 注意:下标从0开始
for (int i = 0; i < m; i++) {
int v = nextInt() - 1;
int w = nextInt() - 1;
int weight = nextInt();
adj[v].add(new Edge(v, w, weight));
}
pq = new PriorityQueue<>();
edgeTo = new Edge[n];
distTo = new int[n];
visited = new boolean[n];
// 初始化
Arrays.fill(distTo, Integer.MAX_VALUE);
distTo[start] = 0;
pq.add(new Pair(start, 0)); // 加入源点
// 因为每次都从 pq 中找到最小的那个 distTo,所以可以用堆优化
// 为什么每次找到最小的distTo[w], 就是找到了源点到达 w 的最短路径?
// 因为我们是对 v-w 进行松弛操作(判断 distTo[w] > distTo[v] + e.weight),最小的 distTo 已经没办法再进行松弛操作了。
// 也就是说 v-w 这些边已经都失效了。已经不可能让 distTo[w] 更小了,所以最小的 distTo[w] 就是源点到 w 的最短路径。
while (! pq.isEmpty()) {
Pair pair = pq.poll();
relax(pair.index);
}
// 输出
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append(distTo[i]).append(" ");
}
pw.println(sb.toString().trim());
pw.flush();
}
// 边的松弛操作, 检查从源点 s 到 w 的最短路径是不是从 s 到 v 再到 w
// distTo[] 只可能变小,单调递减。所以从顶点 w 的所有的前一个顶点 v 都进行松弛操作,就可以得出distTo[w] 的最小值(也就是源点到达 w 的最短路径)。
static void relax(int v) {
// 因为在 pq 中会保留到达某个顶点的很多个 distTo,如果到达这个点的最短路径已经找到,那么这些距离就都失效了
if (visited[v] == true) {
return;
}
visited[v] = true;
for (Edge e : adj[v]) {
int w = e.to();
// 说明 v-w 这条边是有效的,否则这条边失效了
if (distTo[w] > distTo[v] + e.weight) {
distTo[w] = distTo[v] + e.weight;
edgeTo[w] = e;
// 添加进 pq
pq.add(new Pair(w, distTo[w]));
}
}
}
}
/**
* 边结构
*/
class Edge {
int v;
int w;
int weight;
public Edge(int v, int w, int weight) {
this.v = v;
this.w = w;
this.weight = weight;
}
public int from() {
return v;
}
public int to() {
return w;
}
}
/**
* 堆结点
*/
class Pair implements Comparable<Pair>{
int index; // dist : distTo[index] 的值
int dist;
public Pair(int index, int dist) {
this.index = index;
this.dist = dist;
}
@Override
public int compareTo(Pair o) {
return this.dist - o.dist;
}
}