leetcode之最短路刷题总结1

leetcode之最短路刷题总结1

**最短路:**给定两个顶点,在以这两个 顶点为起点和终点的路径中,边的权值和最小的路径。

最短路径中有几种经典的算法,我们主要练习的是Dijkstra算法和Floyd算法,分别用于解决单元最短路径问题和多源最短路径问题。

Dijkstra算法适用于单源最短路径,具体地,找到最短路已经确定的节点,从它出发更新相邻节点的最短距离。

Floyd算法适用于多源最短路径,具体地,通过一系列n阶矩阵来计算一个n节点加权图的最短距离。每轮让第k个节点做中转节点,更新第i个节点到第j个节点的最短路径。

1-阈值距离内邻居最少的城市
题目链接:题目链接戳这里!!!

思路:Floyd算法
本题需要记录每个节点到其它节点的最短路径,所以通过Floyd算法,使用dp数组更新所有节点到其余节点的最短路径,最后dp数组保留的就是所有节点到其余节点的最短路径,使用num数组记录,每个节点到其它节点的最短路长度小于distanceThreshold的个数,找出最少的个数的起点i就是答案。

class Solution {
    public int findTheCity(int n, int[][] edges, int distanceThreshold) {
        //多源最短路径,使用Floyd算法
        int m = edges.length ;
        int [][] dp = new int [n][n] ;//n阶矩阵计算一个n节点的加权图的最短路径
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                dp[i][j] = 1000000000 ;
            }
        }
        for(int i=0; i<n; i++){
            dp[i][i] = 0 ;
        }

        for(int [] edge : edges){
            int u=edge[0], v = edge[1], w = edge[2] ;
            dp[u][v] = dp[v][u] = w ;
        }

        for(int k=0; k<n; k++){ //k作为中转节点,更新i节点到j节点的最短路径
            for(int i=0; i<n; i++){
                for(int j=0; j<n; j++){
                    dp[i][j] = Math.min(dp[i][j], dp[i][k]+dp[k][j]) ;
                }
            }
        }

        int [] num = new int [n] ; //记录所有节点可到达的路径数量
        int min = 1000000000, pos = -1 ;

        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                if(i==j){
                    continue ;
                }
                if(dp[i][j] <= distanceThreshold){
                    num[i] ++ ;
                }
            }
            if(min >= num[i]){
                min = num[i] ;
                pos = i ;
            }
        }
        return pos ;
    }
}

在这里插入图片描述
2-到达目的地的方案数
题目链接:题目链接戳这里!!!

思路:Dijkstra算法
这是个单源最短路问题,可以使用Dijkstra算法,dist[i][0]记录到达第i个节点的最短路径长度,dist[i][1]记录到达第i个节点的最短路径条数。

使用优先队列,对所用时间按升序排序,如果时间相等按节点大小升序排序。每次选一个用时最短的,如果通过原始节点到达该节点的距离小于初始值,则更新最短距离,如果节点的最短路已经确定,则直接跳过。

class Solution {
    public int countPaths(int n, int[][] roads) {
        //花费最少时间从路口0到路口n-1的方案数,单源最短路径问题,可以使用Dijkstra
        final int INF = Integer.MAX_VALUE ;
        int mod = 1000000007 ;
        List<int []> []g = new List[n] ;
        for(int i=0; i<n; i++){
            g[i] = new ArrayList<int []>() ;
        }
        for(int [] road : roads){
            int u = road[0], v = road[1], w = road[2] ;
            g[u].add(new int [] {v,w}) ;
            g[v].add(new int [] {u,w}) ;
        }
        int [][] dist = new int [n][2] ;
        for(int i=0; i<n; i++){
            dist[i][0] = INF ;
        }

        dist[0][0] = 0 ; //到达0节点的最短路径长度
        dist[0][1] = 1 ; //到达0节点的最短路条数
        

        PriorityQueue<int []> queue = new PriorityQueue<int []>((a,b)->a[1]!=b[1]?a[1]-b[1]:a[0]-b[0]) ;
        queue.add(new int []{0,0}) ; //数组中的两个值,一个是到达的节点和时间
        while(!queue.isEmpty()){
            int [] p = queue.poll() ;
            int x = p[0], time = p[1] ;
            if(dist[x][0] < time){ //最短路已经确定
                continue ;
            }
            for(int [] edge : g[x]){
                int y = edge[0], d = dist[x][0] + edge[1] ;
                if(d==dist[y][0]){
                    dist[y][1] += dist[x][1] ;
                    dist[y][1] = dist[y][1] % mod ;
                }
                if(d<dist[y][0]){ //更新最短距离
                   dist[y][0] = d ;
                   queue.add(new int []{y, d}) ;
                   dist[y][1] = dist[x][1] ;
                }
            }
        }
        return dist[n-1][1] ;
    }
}

在这里插入图片描述
思路2:当然这个题也可以用Floyd去解决,具体如下:

dist[i][j]记录从i到j的最短路长度
ways[i][j]记录从i到j最短路的条数
每轮让第k个节点做中转节点,更新第i个节点到第j个节点的最短路径。

由于Floyd是O(n^3)复杂度,显然不如Dijkstra效率高。

class Solution {
    public int countPaths(int n, int[][] roads) {
       if(n<=2){
           return 1 ;
       }
       int mod = 1000000007 ;
       long [][] dist = new long [n][n] ;
       long [][] ways = new long [n][n] ;

       for(int i=0; i<n; i++){
           Arrays.fill(dist[i],Long.MAX_VALUE) ;
       }

       for(int i=0; i<n; i++){
           dist[i][i] = 0 ;
       }

       for(int [] edge : roads){
           int u = edge[0], v = edge[1], w = edge[2] ;
           dist[u][v] = dist[v][u] = w ;
           ways[u][v] = ways[v][u] = 1 ;
       }

       for(int k=0; k<n; k++){
           for(int i=0; i<n; i++){
               if(dist[i][k]==Long.MAX_VALUE){
                   continue ;
               }
               for(int j=0; j<n; j++){
                   if(dist[k][j] == Long.MAX_VALUE){
                       continue ;
                   }
                   if(dist[i][j] < dist[i][k] + dist[k][j]){
                       continue ;
                   }
                   if(dist[i][j] > dist[i][k] + dist[k][j]){
                       //遇到最短路
                       ways[i][j] = ways[i][k] * ways[k][j] ;
                   }else{
                       //遇到相等的最短路
                       ways[i][j] += ways[i][k] * ways[k][j] ;
                   }
                    ways[i][j] = ways[i][j] % mod ;
                    dist[i][j] = dist[i][k] + dist[k][j] ;
               }
           }
       }
       return (int) ways[0][n-1] ;
    }
}

在这里插入图片描述
3-网络延迟时间
题目链接:题目链接戳这里!!!

思路:Dijkstra算法
单源最短路径问题,可以使用Dijkstra算法计算出k点到其它点的最短路径,然后取所有最短路的最大值。

class Solution {
   public int networkDelayTime(int[][] times, int n, int k) {
     //单源最短路径问题,可以使用Dijkstra算法计算出k点到其它点的最短路径,然后取所有最短路的最大值
     int [][] g = new int [n][n] ;
     int [] dist = new int [n] ;
     int INF = Integer.MAX_VALUE / 2;
     for(int i=0; i<n; i++){
         Arrays.fill(g[i], INF) ;
     }
     for(int [] edge : times){
         int u = edge[0]-1, v = edge[1]-1, w = edge[2] ;
         g[u][v] = w ;
     }
     Arrays.fill(dist,INF) ;
     dist[k-1] = 0 ;
   
     boolean [] vis = new boolean[n] ; //记录当前节点是否找出最短路径
      
     for(int i=0; i<n; i++){
         int x = -1 ;
         for(int y=0; y<n; y++){
             if(!vis[y] && (x==-1 || dist[x]>dist[y])){
                 x = y ;
             }
         }
         vis[x] = true ;
         for(int y=0; y<n; y++){
             dist[y] = Math.min(dist[y], dist[x]+g[x][y]) ;
         }
     }
     int ans = Integer.MIN_VALUE ;
     for(int d : dist){
         ans = Math.max(ans, d) ;
     }
     return ans == INF ? -1 : ans ;

    }
}

在这里插入图片描述
4-概率最大的路径
题目链接:题目链接戳这里!!!

思路:用Dijkstra求单源最大概率路径,和求最短路径一样的思路。只不过优先队列按照最大的权值排序,每次取出的是最大权值的,找更大权值的去更新当前的权值。

class Solution {
    public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
        //Dijkstra算法,找出最大的概率路径的概率
  
        List<double[]> [] g = new List[n] ;

        for(int i=0; i<n; i++){
            g[i] = new ArrayList<double[]>() ;
        }

        for(int i=0; i<edges.length; i++){
            int u =edges[i][0], v = edges[i][1] ;
            double w = succProb[i] ;
            g[u].add(new double [] {v, w}) ;
            g[v].add(new double [] {u, w}) ;
        }

       
        //自定义排序,优先更显概率大的节点
        PriorityQueue<State> queue = new PriorityQueue<State>((a,b)->{
            if(a.pro>b.pro){
                return -1;
            }else if(a.pro<b.pro){
                return 1 ;
            }else {
                return 0 ;
            }
        }) ; 

        double [] ans = new double [n] ;
        ans[start] = 1.0 ;
        queue.add(new State(start,1)) ;
        while(!queue.isEmpty()){
            State curNode = queue.poll() ;
            if(curNode.id==end){
                return curNode.pro ;
            }
            for(double [] neigbors : g[curNode.id]){
                int nextId = (int)neigbors[0] ;
                double nextPro = curNode.pro * neigbors[1] ;
                if(nextPro > ans[nextId]){
                    ans[nextId] = nextPro ;
                    queue.add(new State(nextId, nextPro)) ;
                }
            }
        }
        return ans[end] ;
      
    }
}
class State{
    int id ;
    double pro ;
    public State(int id, double pro){
        this.id = id ;
        this.pro = pro ;
    }
}

在这里插入图片描述
**

每走的一步路都算数,日拱一卒无有尽,功不唐捐终入海!!!

**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuist__NJUPT

给个鼓励吧,谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值