最短路径的模板-摘自宫水三叶

leetcode 743题

一、存图方式

1、邻接矩阵

int[][] w = new int[N][N];

void add(int a, int b, int c){
    w[a][b] = c;
}

2、邻接表(链式前向星存图)

int[] head = new int[N]; //存储某个结点所对应边集合(链表)中最后一条边的头结点在数组中的位置
int[] edge = new int[M]; //某条边指向的另一个结点
int[] next = new int[M]; //以链表的形式存储边,存储同起点的边集合
int[] w = new int[M]; //边的权重
int index; //边的索引

void add(int a, int b, int c){
    edge[index] = b;
    next[index] = head[a];
    head[a] = index; //头插法
    w[index] = c;
    index++;
}

//遍历同起点的边
for(int i = head[a]; i != -1; i = next[i]){
    int b = edge[i];
    int c = w[i];
}

3、类存图

二、Floyd算法

1、邻接矩阵

class Solution{
    int N = 110, M = 6010;
    int[][] w = new int[N][M];
    int INF = 0x3f3f3f3f;
    int n, k;
    
    public int networkDelayTime(int[][] ts, int _, int _k){
        n = _n;
        k = _k;
        //初始化邻接矩阵
        for(int i = 1; i <= n; i++){
            for(int j = 1;j <= n; j++){
                w[i][j] = w[j][i] = i == j ? 0 : INF;
            }
        }
        //存图
        for(int[] t : ts){
            w[t[0]][t[1]] = t[2];
        }
        //最短路径
        floyd();
        //遍历答案
        int ans = 0;
        for(int i = 1; i <= n; i++){
            ans = Math.max(ans,w[k][i]);
        }
        return ans >= INF / 2; -1 : ans;
    }
    
    public void floyd(){
        //floyd 算法模板
        //考虑加入某个中转点之后路径的权重
        for(int p = 1; p <= n; p++){
            for(int i = 1; i<= n; i++){
                for(int j = 1; j <= n; j++){
                    w[i][j] = Math.min(w[i][j],w[i][p] + w[p][j]);
                }
            }
        }
    }
}

三、朴素Dijksta

1、邻接矩阵

class Solution {
    int N = 110, M = 6010;
    // 邻接矩阵数组:w[a][b] = c 代表从 a 到 b 有权重为 c 的边
    int[][] w = new int[N][N];
    // dist[x] = y 代表从「源点/起点」到 x 的最短距离为 y
    int[] dist = new int[N];
    // 记录哪些点已经被更新过
    boolean[] vis = new boolean[N];
    int INF = 0x3f3f3f3f;
    int n, k;
    public int networkDelayTime(int[][] ts, int _n, int _k) {
        n = _n; k = _k;
        // 初始化邻接矩阵
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                w[i][j] = w[j][i] = i == j ? 0 : INF;
            }
        }
        // 存图
        for (int[] t : ts) {
            int u = t[0], v = t[1], c = t[2];
            w[u][v] = c;
        }
        // 最短路
        dijkstra();
        // 遍历答案
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = Math.max(ans, dist[i]);
        }
        return ans > INF / 2 ? -1 : ans;
    }
    
    public void dijkstra(){
        //先将所有的点标记为 未更新 和 距离为正无穷
        Arrays.fill(vis,false);
        Arrays.fill(dist,INF);
        //起点距离为0
        dist[k] = 0;
        //迭代n次
        for(int p = 1; p <= n; p++){
            int t = -1;
            //找到最小距离的点
            for(int i = 1; i <= n; i++){
                if(!vis[i] && (t == -1 || dist[i] < dist[t])) t = i;
            }
            //标记t 为已更新
            vis[t] = true;
            //用t的最小距离更新其他点
            for(int i = 1; i <= n; i++){
                dist[i] = Math.min(dist[i],dist[t] + w[t][i]);
            }
        }   
    }

}

四、堆优化Dijkstra

class Solution {
    int N = 110, M = 6010;
    // 邻接表
    int[] he = new int[N], e = new int[M], ne = new int[M], w = new int[M];
    // dist[x] = y 代表从「源点/起点」到 x 的最短距离为 y
    int[] dist = new int[N];
    // 记录哪些点已经被更新过
    boolean[] vis = new boolean[N];
    int n, k, idx;
    int INF = 0x3f3f3f3f;
    void add(int a, int b, int c) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx;
        w[idx] = c;
        idx++;
    }
    public int networkDelayTime(int[][] ts, int _n, int _k) {
        n = _n; k = _k;
        // 初始化链表头
        Arrays.fill(he, -1);
        // 存图
        for (int[] t : ts) {
            int u = t[0], v = t[1], c = t[2];
            add(u, v, c);
        }
        // 最短路
        dijkstra();
        // 遍历答案
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = Math.max(ans, dist[i]);
        }
        return ans > INF / 2 ? -1 : ans;
    }
    void dijkstra() {
        // 起始先将所有的点标记为「未更新」和「距离为正无穷」
        Arrays.fill(vis, false);
        Arrays.fill(dist, INF);
        // 只有起点最短距离为 0
        dist[k] = 0;
        // 使用「优先队列」存储所有可用于更新的点
        // 以 (点编号, 到起点的距离) 进行存储,优先弹出「最短距离」较小的点
        PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->a[1]-b[1]);
        q.add(new int[]{k, 0});
        while (!q.isEmpty()) {
            // 每次从「优先队列」中弹出
            int[] poll = q.poll();
            int id = poll[0], step = poll[1];
            // 如果弹出的点被标记「已更新」,则跳过
            if (vis[id]) continue;
            // 标记该点「已更新」,并使用该点更新其他点的「最短距离」
            vis[id] = true;
            for (int i = he[id]; i != -1; i = ne[i]) {
                int j = e[i];
                if (dist[j] > dist[id] + w[i]) {
                    dist[j] = dist[id] + w[i];
                    q.add(new int[]{j, dist[j]});
                }
            }
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值