[leetcode]——四种图的最短路径算法

前言

leetcode 743. Netword Delay Time为例。该题是要求一个有向图中,从某一个点开始,到达所有点的最短路径中的最大值。

图的最短路算法

单源最短路

不带负权边:Dijkstra

思路:确定一个出发点,每一个时间步,从所有没有到达过的点中,选择一个与出发点距离最短的点(如果不存在,则结束),此距离就是出发点到该点的最短距离。把该点设置为已到达,并将该点作为中间点,更新出发点和剩余未到达点的最短距离。注意两点: 1. 为什么"此距离就是出发点到该点的最短距离",因为如果存在另一条路径,使得出发点到该点距离更短,那首先需要满足的是:出发点到下一个点的距离一定是小于这个最短距离的,那如果是小于的,当前时间步为什么不选择这个点呢? 2. 如果存在负边,Dijkstra不成立。

代码(100%, 21.91%):

class Solution {
    
    
    int[][] graph;
    int MAX_VALUE = Integer.MAX_VALUE / 2;
    public int networkDelayTime(int[][] times, int n, int k) {
    
    
        graph = new int[n+1][n+1];
        for(int i = 1; i <= n; i++){
    
    
            for(int j = 1; j <= n; j++){
    
    
                graph[i][j] = i == j ? 0 : MAX_VALUE;
            }
        }
        for(int[] edge:times){
    
    
            int u = edge[0];
            int v = edge[1];
            int w = edge[2];
            graph[u][v] = w;
        }
        int[] dis_lst = graph[k]; //这样graph本身也会被改变,不过不影响
        boolean[] vis = new boolean[n+1];
        vis[k] = true;
        for(int i = 1; i < n; i++){
    
    
            int min_dis = MAX_VALUE;
            int v = -1;
            for(int j = 1; j <= n; j++){
    
    
                if(!vis[j] && dis_lst[j] < min_dis){
    
    
                    min_dis = dis_lst[j];
                    v = j;
                }
            }
            if(v == -1){
    
    
                break;
            }
            vis[v] = true;
            for(int j = 1; j <= n; j++){
    
    
                if(!vis[j]){
    
    
                    dis_lst[j] = Math.min(dis_lst[j], dis_lst[v] + graph[v][j]);
                }
            }
        }
        int ans = 0;
        for(int dis:dis_lst){
    
    
            ans = Math.max(ans, dis);
        }
        return ans == MAX_VALUE ? -1 : ans;
    }
}

算法复杂度:

  • 时间:O(N^2+E)
  • 空间:O(N^2)

带负权边:Bellman-Ford\SPFA

Bellman-Ford:
思路:最多需要循环n-1次,每一次遍历所有边,进行更新。第i次的更新,得到的是:出发点最多经过i条边,到达各点的最短路径。
两种优化方式:

  • 某一次没有发生更新,直接停
  • 每一次只选择特定的边进行更新,即:如果上一次的一条边u->v更新成功,则这一次v的出边是需要考虑用来进行更新的。(SPFA)

代码:(Bellman-Ford)(89.76%,91.12%)

class Solution {
    
    
    int MAX_VALUE = Integer.MAX_VALUE / 2;
    public int networkDelayTime(int[][] times, int n, int k) {
    
    
        /* 不需要建图,直接使用边即可
        graph = new int[n+1][n+1];
        for(int i = 1; i <= n; i++){
    
    
            for(int j = 1; j <= n; j++){
    
    
                graph[i][j] = i == j ? 0 : MAX_VALUE;
            }
        }
        for(int[] edge:times){
    
    
            int u = edge[0];
            int v = edge[1];
            int w = edge[2];
            graph[u][v] = w;
        }
        */
        int[] dis_lst = new int[n+1];
        Arrays.fill(dis_lst, MAX_VALUE);
        dis_lst[k] = 0;
        dis_lst[0] = 0; //否则出错
        for(int x = 0; x < n; x ++){
    
    
            int flag = 0;
            for(int[] edge:times){
    
    
                int u = edge[0];
                int v = edge[1];
                int w = edge[2];
                if(dis_lst[v] > dis_lst[u] + w){
    
    
                    dis_lst[v] = dis_lst[u] + w;
                    flag = 1;
                }
            }
            if(flag == 0){
    
     // 优化1:没有更新,直接结束
                break;
            }
        }
        int ans = 0;
        for(int dis:dis_lst){
    
    
            ans = Math.max(ans, dis);
        }
        return ans == MAX_VALUE? -1 : ans;
    }
}

算法复杂度:

  • 时间:O(N*E)
  • 空间:O(E)

SPFA:思路,在bellman-ford的基础上,每一次只考虑特定的边,即优化2。使用队列来维护。

代码(89.76%,52.70%):


class Solution {
    
    
    int[][] graph;
    int MAX_VALUE = Integer.MAX_VALUE / 2;
    public int networkDelayTime(int[][] times, int n, int k) {
    
    
        graph = new int[n+1][n+1];
        for(int i = 1; i <= n; i++){
    
    
            for(int j = 1; j <= n; j++){
    
    
                graph[i][j] = i == j ? 0 : MAX_VALUE;
            }
        }
        for(int[] edge:times){
    
    
            int u = edge[0];
            int v = edge[1];
            int w = edge[2];
            graph[u][v] = w;
        }
        int[] dis_lst = new int[n+1];
        Arrays.fill(dis_lst, MAX_VALUE);
        dis_lst[0] = dis_lst[k] = 0;
        Queue<Integer> q= new LinkedList<Integer>();
        q.offer(k);
        while(!q.isEmpty()){
    
    
            int now = q.poll();
            //遍历该点出发的所有边,进行更新
            for(int i = 1; i <= n; i ++){
    
    
                if(dis_lst[i] > dis_lst[now] + graph[now][i]){
    
    
                    dis_lst[i] = dis_lst[now] + graph[now][i];
                    q.offer(i);
                }
            }
        }
        int ans = 0;
        for(int dis:dis_lst){
    
    
            ans = Math.max(ans, dis);
        }
        return ans == MAX_VALUE? -1 : ans;
    }
}

算法复杂度:
不会

多源最短路:Floyd

思路:对于G中任意两个顶点v、w,遍历除这两点以外的所有其它点,假设以该点为中间点,更新v和w之间的最短距离。

代码:(58%, 15%)

/*
返回出发点,到达所有点的最短路径中的最大值
*/
class Solution {
    
    
    int[][] graph;
    int MAX_VALUE = Integer.MAX_VALUE / 2; //防止下面graph[i][x] + graph[x][j]的时候,数值超过上限,反而变小
    public int networkDelayTime(int[][] times, int n, int k) {
    
    
        //建图
        graph = new int[n+1][n+1];
        for(int i = 1; i <= n; i++){
    
    
            for(int j = 1; j <= n; j++){
    
    
                graph[i][j] = i == j ? 0 : MAX_VALUE;
            }
        }
        for(int[] edge:times){
    
    
            int u = edge[0];
            int v = edge[1];
            int w = edge[2];
            graph[u][v] = w;
        }
        //floyd
        for(int x = 1; x <= n; x++){
    
    
            for(int i = 1; i <= n; i++){
    
    
                for(int j = 1; j <= n; j++){
    
    
                        graph[i][j] = Math.min(graph[i][j], graph[i][x] + graph[x][j]);
                }
            }
        }
        //最大的最短路径 
        int ans = 0;
        for(int dis:graph[k]){
    
      //这里要注意graph[k][0]这个位置的值,千万不要初始化为MAX_VALUE
            ans = Math.max(ans, dis);
        }
        return ans == MAX_VALUE ? -1 : ans;
    }
}

算法复杂度:

  • 时间:O(N^3)
  • 空间:O(N^2 + E)

参考

看完就懂了!一篇搞定图论最短路径问题
Java 版 Dijkstra、SPFA、Floyd 逐行解释

Guess you like

Origin blog.csdn.net/jokerxsy/article/details/116919988