leetcode之最短路刷题总结1

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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] = leetcode之分治刷题总结1

八年级上册轴对称之最短路径问题一

Leetcode刷题100天—23. 合并K个升序链表(优先队列)—day18

图论算法之最短路径

图论之最短路径floyd算法

[JavaScript 刷题] 搜索 - 计算在网格中从原点到特定点的最短路径长度 Leetcode 1091