LeetCode Ts-2642. Design Graph With Shortest Path Calculator

    CodingLeetCode Js-Graph

    There is a directed weighted graph that consists of n nodes numbered from 0 to n – 1. The edges of the graph are initially represented by the given array edges where edges[i] = [fromi, toi, edgeCosti] meaning that there is an edge from fromi to toi with the cost edgeCosti.
    Implement the Graph class:
    Graph(int n, int[][] edges) initializes the object with n nodes and the given edges.
    addEdge(int[] edge) adds an edge to the list of edges where edge = [from, to, edgeCost]. It is guaranteed that there is no edge between the two nodes before adding this one.
    int shortestPath(int node1, int node2) returns the minimum cost of a path from node1 to node2. If no path exists, return -1. The cost of a path is the sum of the costs of the edges in the path.

    這裡有一個有方向且帶有權重的圖,它包含了 n 個節點,節點編號從 0 ~ n - 1。
    圖中的邊(edges)一開始由給予的陣列 edges 來表示,其中 edges[i] = [fromi, toi, edgeCosti],
    代表從節點 fromi 指向節點 to_i,且該邊的成本為 edgeCosti。
    請實作 Graph class: 
    Graph(int n, int[][] edges) 初始化一個有 n 個節點和給予邊的物件。
    addEdgge(int[] edge) 將一條新的邊新增到圖中,該邊的格式為 [from, to, edgeCost],
    並保證在新增這條邊之前,這兩個節點之間不存在任何的邊。
    shortestPath(int node1, int node2) 回傳從 node1 到 node2 的最小路徑成本,
    如果不存在任何可到達的路徑,則回傳 -1。
    路徑成本為路徑中所有邊的權重總和。
    

    Example 1:

    Input
    ["Graph", "shortestPath", "shortestPath", "addEdge", "shortestPath"]
    [[4, [[0, 2, 5], [0, 1, 2], [1, 2, 1], [3, 0, 3]]], [3, 2], [0, 3], [[1, 3, 4]], [0, 3]]
    Output
    [null, 6, -1, null, 6]
    
    Explanation
    Graph g = new Graph(4, [[0, 2, 5], [0, 1, 2], [1, 2, 1], [3, 0, 3]]);
    g.shortestPath(3, 2); // return 6. The shortest path from 3 to 2 in the first diagram above is 3 -> 0 -> 1 -> 2 with a total cost of 3 + 2 + 1 = 6.
    g.shortestPath(0, 3); // return -1. There is no path from 0 to 3.
    g.addEdge([1, 3, 4]); // We add an edge from node 1 to node 3, and we get the second diagram above.
    g.shortestPath(0, 3); // return 6. The shortest path from 0 to 3 now is 0 -> 1 -> 3 with a total cost of 2 + 4 = 6.
    

    Solution:
    我們先在 Graph class 裡面建立 private 的 n(0 ~ n -1 個節點數量),
    以及 dis(distance) 的數值陣列。
    接著我們建立一個 n x n 的陣列,初始值假設為最大的安全值,
    以及 dis 中自己到自己的距離為 0,接著就可以把例子中的初始陣列填入,
    我們透過 Floyd-Warshall 對每個節點檢查最短的路徑,
    這樣就完成了我們的 constructor。
    在建立 addEdge 時,我們需要新增一條邊 from -> to (cost),
    因為我們建構子時有跑過最短的距離,所以 dis[i][from] 與 dis[to][j] 再加上 cost,
    就可以和原有的 dis[i][j] 比較取最小值。
    再來就是 shortestPath() 的部分,我們從 dis 讀出 node1 -> node2。
    如果是 INF ,則回傳 -1,表示不可達到,如不是 INF,則回傳距離。

    Code 1: BigO(n ^ 3)

    class Graph {
        private n: number;
        private dis: number[][];
    
        constructor(n: number, edges: number[][]) {
            const INF = Infinity;
            this.n = n;
            this.dis = Array.from({length: n}, () => Array(n).fill(INF));
            for (let i = 0; i < n; i++) {
                this.dis[i][i] = 0;
            }    
            for (const edge of edges) {
                const [from, to, cost] = edge;
                this.dis[from][to] = Math.min(this.dis[from][to], cost);
            }
            // Floyd-Warshall
           for (let i = 0; i < n; i++) {
                for (let j = 0; j < n; j++) {
                    for (let k = 0; k < n; k++) {
                        this.dis[j][k] = Math.min(this.dis[j][k], this.dis[j][i] + this.dis[i][k]);
                    }
                }
            }
        }
    
        addEdge(edge: number[]): void {
            const INF = Infinity;
            const [from, to, cost] = edge;         
            for (let i = 0; i < this.n; i++) {
                for (let j = 0; j < this.n; j++) {
                    this.dis[i][j] = Math.min(
                        this.dis[i][j],
                        this.dis[i][from] + cost + this.dis[to][j]
                    );
                }
            }
        }
    
        shortestPath(node1: number, node2: number): number {
            const INF = Infinity;
            return this.dis[node1][node2] === INF ? -1 : this.dis[node1][node2];
        }
    }
    
    /**
     * Your Graph object will be instantiated and called as such:
     * var obj = new Graph(n, edges)
     * obj.addEdge(edge)
     * var param_2 = obj.shortestPath(node1,node2)
     */
    

    FlowChart:
    Example 1

    Input
    ["Graph", "shortestPath", "shortestPath", "addEdge", "shortestPath"]
    [[4, [[0, 2, 5], [0, 1, 2], [1, 2, 1], [3, 0, 3]]], [3, 2], [0, 3], [[1, 3, 4]], [0, 3]]
    
    Initial distance matrix:
    	Node0	Node1	Node2	Node3	
    Node0	0	2	5	INF	
    Node1	INF	0	1	INF	
    Node2	INF	INF	0	INF	
    Node3	3	INF	INF	0	
    
    
    After considering node 0 as intermediate:
    	Node0	Node1	Node2	Node3	
    Node0	0	2	5	INF	
    Node1	INF	0	1	INF	
    Node2	INF	INF	0	INF	
    Node3	3	5	8	0	
    
    
    After considering node 1 as intermediate:
    	Node0	Node1	Node2	Node3	
    Node0	0	2	3	INF	
    Node1	INF	0	1	INF	
    Node2	INF	INF	0	INF	
    Node3	3	5	6	0	
    
    
    After considering node 2 as intermediate:
    	Node0	Node1	Node2	Node3	
    Node0	0	2	3	INF	
    Node1	INF	0	1	INF	
    Node2	INF	INF	0	INF	
    Node3	3	5	6	0	
    
    
    After considering node 3 as intermediate:
    	Node0	Node1	Node2	Node3	
    Node0	0	2	3	INF	
    Node1	INF	0	1	INF	
    Node2	INF	INF	0	INF	
    Node3	3	5	6	0	
    
    
    After adding edge [1,3,4]:
    	Node0	Node1	Node2	Node3	
    Node0	0	2	3	6	
    Node1	7	0	1	4	
    Node2	INF	INF	0	INF	
    Node3	3	5	6	0