单源最短路径 Dijkstra 算法 Java

洛谷 P4779 单源最短路径

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值