判斷某節點是否和其餘節點聯通並計算時延 Network Delay Time

問題:node

There are N network nodes, labelled 1 to N.算法

Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node, v is the target node, and w is the time it takes for a signal to travel from source to target.數組

Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1.網絡

Note:spa

  1. N will be in the range [1, 100].
  2. K will be in the range [1, N].
  3. The length of times will be in the range [1, 6000].
  4. All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 1 <= w <= 100.

解決:code

①  圖的遍歷。給定一張圖中若干節點的連通性信息以及節點間的距離,以後要求咱們判斷針對某個特定節點(第K個節點),其可否和其餘全部結點連通,並求出要到達全部節點須要通過的網絡時延。隊列

通過分析,能夠明確兩個子問題,即: 
- 判斷兩個節點之間是否連通 
- 求出某個節點到圖中其餘任意可達節點所需的最短路徑get

所以本題的最終目的是求單源最短路徑。it

最短路徑的經常使用解法有迪傑克斯特拉算法Dijkstra Algorithm, 弗洛伊德算法Floyd-Warshall Algorithm, 和貝爾曼福特算法Bellman-Ford Algorithm,其中,Floyd算法是多源最短路徑,即求任意點到任意點到最短路徑,而Dijkstra算法和Bellman-Ford算法是單源最短路徑,即單個點到任意點到最短路徑。io

Dijkstra算法處理有向權重圖時,權重必須爲正,而另外兩種能夠處理負權重有向圖,可是不能出現負環,所謂負環,就是權值總和均爲負的環

這三個算法的核心思想,當有對邊 (u, v) 是結點u到結點v,若是 dist(v) > dist(u) + w(u, v),那麼 dist(v) 就能夠被更新,這是全部這些的算法的核心操做。

Dijkstra算法是從一個頂點到其他各頂點的最短路徑算法,解決的是有向圖中最短路徑問題。迪傑斯特拉算法主要特色是以起始點爲中心向外層層擴展,直到擴展到終點爲止,是一種廣度優先的搜索方法。

普通的實現方法的時間複雜度爲O(V^2),基於優先隊列的實現方法的時間複雜度爲O(E + VlogV),其中V和E分別爲結點和邊的個數。

dijkstra算法例子:求從結點0到各個結點的最短路徑。

  • 使用Map <Integer,Map <Integer,Integer >>來存儲源節點,目標節點和它們之間的距離。
  • 將節點K存儲到PriorityQueue。
  • 而後繼續獲取到當前節點的最近節點,並計算從源(K)到該節點(絕對距離)的距離。 使用map存儲每一個節點的最短絕對距離。
  • 返回絕對距離最大的節點所耗費的時間。

class Solution { //137ms
    public int networkDelayTime(int[][] times, int N, int K) {
        if (times == null || times.length == 0) return -1;
        Map<Integer,Map<Integer,Integer>> path = new HashMap<>();//key爲起始節點,value爲相鄰節點和兩個節點的距離
        for (int[] time : times){
            Map<Integer,Integer> sourceMap = path.get(time[0]);
            if (sourceMap == null){
                sourceMap = new HashMap<>();
                path.put(time[0],sourceMap);
            }
            Integer distance = sourceMap.get(time[1]);
            if (distance == null || distance > time[2]){
                sourceMap.put(time[1],time[2]);
            }
        }
        //使用PriorityQueue獲取絕對距離最短的節點,並計算其與鄰居節點的絕對距離。
        Map<Integer,Integer> distanceMap = new HashMap<>();
        distanceMap.put(K,0);
        PriorityQueue<int[]> priorityQueue = new PriorityQueue<>((i1,i2) -> {return i1[1] - i2[1];});
        priorityQueue.offer(new int[]{K,0});
        int max = -1;
        while (! priorityQueue.isEmpty()){
            int[] cur = priorityQueue.poll();
            int node = cur[0];
            int distance = cur[1];
            if (distanceMap.containsKey(node) && distanceMap.get(node) < distance) continue;
            Map<Integer,Integer> sourceMap = path.get(node);
            if (sourceMap == null) continue;
            for (Map.Entry<Integer,Integer> entry : sourceMap.entrySet()){
                int absoluteDistance = distance + entry.getValue();
                int targetNode = entry.getKey();
                if (distanceMap.containsKey(targetNode) && distanceMap.get(targetNode) <= absoluteDistance) continue;
                distanceMap.put(targetNode,absoluteDistance);
                priorityQueue.offer(new int[]{targetNode,absoluteDistance});
            }
        }
        for (int val : distanceMap.values()){
            if (val > max){
                max = val;
            }
        }
        return distanceMap.size() == N ? max : -1;
    }

② 使用floyd-warshall算法獲得全部的最短路徑,而後選擇從節點K開始的路徑,這樣更容易(但效率更低)。時間複雜度爲O(n^3),空間複雜度爲O(n^2)。

1.  時間複雜度的對比

2. 

 

3. 例如:

D0表示圖的鄰接矩陣的表示,D1由上面的公式求得,該公式與迪傑斯特拉算法同樣。

P數組表示當前節點的前驅節點。

class Solution { //67ms
    public int networkDelayTime(int[][] times, int N, int K) {
        int maxDistance = 100 * 100;//兩個節點之間的最短距離
        int[][] distance = new int[N][N];
        for (int i = 0;i < N;i ++){//初始化全部節點之間的距離爲最遠距離
            Arrays.fill(distance[i],maxDistance);
        }
        //處理圖的信息,將能夠到達的點之間的路徑距離存入表中
        for (int[] time : times){
            distance[time[0] - 1][time[1] - 1] = time[2];
        }
        for (int i = 0;i < N;i ++){
            distance[i][i] = 0;
        }
        //使用弗洛伊德算法遍歷圖,更新節點的路徑距離
        for (int k = 0;k < N;k ++){
            for (int i = 0;i < N;i ++){
                for (int j = 0;j < N;j ++){
                    distance[i][j] = Math.min(distance[i][j],distance[i][k] + distance[k][j]);
                }
            }
        }
        //尋找節點K到最遠節點的最短距離
        int res = Integer.MIN_VALUE;
        for (int i = 0;i < N;i ++){
            if (distance[K - 1][i] >= maxDistance) return -1;//節點不可達
            res = Math.max(res,distance[K - 1][i]);
        }
        return res;
    }
}

③ 使用Bellman-Ford算法,時間複雜度爲O(VE),其中V爲圖中節點數,E爲圖中邊數;空間複雜度爲O(V)。

Bellman-Ford算法從源點逐次繞過其餘節點,以縮短到達終點的最短路徑長度。

dist數組的遞推公式:

class Solution { //74ms     public int networkDelayTime(int[][] times, int N, int K) {         int maxDistance = 100 * 100;//兩個節點之間的最短距離         int[] distance = new int[N];         Arrays.fill(distance,maxDistance);//初始化全部節點之間的距離爲最遠距離         distance[K - 1] = 0;//第K個點做爲起點         for (int i = 1;i < N;i ++) {             for (int[] time : times) {//使用Bellman-Ford計算最短路徑                 int u = time[0] - 1;                 int v = time[1] - 1;                 int w = time[2];                 distance[v] = Math.min(distance[v], distance[u] + w);             }         }         int res = 0;         for (int i = 0;i < distance.length;i ++){//尋找最遠的路徑             res = Math.max(res,distance[i]);         }         return res == maxDistance ? -1 : res;     } }

相關文章
相關標籤/搜索