2642. 设计可以求最短路径的图类
给你一个有 n
个节点的 有向带权 图,节点编号为 0
到 n - 1
。图中的初始边用数组 edges
表示,其中 edges[i] = [fromi, toi, edgeCosti]
表示从 fromi
到 toi
有一条代价为 edgeCosti
的边。
请你实现一个 Graph
类:
Graph(int n, int[][] edges)
初始化图有 n
个节点,并输入初始边。addEdge(int[] edge)
向边集中添加一条边,其中 edge = [from, to, edgeCost]
。数据保证添加这条边之前对应的两个节点之间没有有向边。int shortestPath(int node1, int node2)
返回从节点 node1
到 node2
的路径 最小 代价。如果路径不存在,返回 -1
。一条路径的代价是路径中所有边代价之和。
示例 1:
输入: ["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]] 输出: [null, 6, -1, null, 6] 解释: Graph g = new Graph(4, [[0, 2, 5], [0, 1, 2], [1, 2, 1], [3, 0, 3]]); g.shortestPath(3, 2); // 返回 6 。从 3 到 2 的最短路径如第一幅图所示:3 -> 0 -> 1 -> 2 ,总代价为 3 + 2 + 1 = 6 。 g.shortestPath(0, 3); // 返回 -1 。没有从 0 到 3 的路径。 g.addEdge([1, 3, 4]); // 添加一条节点 1 到节点 3 的边,得到第二幅图。 g.shortestPath(0, 3); // 返回 6 。从 0 到 3 的最短路径为 0 -> 1 -> 3 ,总代价为 2 + 4 = 6 。
提示:
1 <= n <= 100
0 <= edges.length <= n * (n - 1)
edges[i].length == edge.length == 3
0 <= fromi, toi, from, to, node1, node2 <= n - 1
1 <= edgeCosti, edgeCost <= 106
addEdge
至多 100
次。shortestPath
至多 100
次。原站题解
javascript 解法, 执行用时: 178 ms, 内存消耗: 57.1 MB, 提交时间: 2024-03-26 09:34:05
var Graph = function(n, edges) { const g = Array.from({length: n}, () => Array(n).fill(Infinity)); // 邻接矩阵 for (const [x, y, w] of edges) { g[x][y] = w; // 添加一条边(题目保证没有重边) } this.addEdge = function(e) { g[e[0]][e[1]] = e[2]; // 添加一条边(题目保证这条边之前不存在) } this.shortestPath = function(start, end) { // dis[i] 表示从起点 start 出发,到节点 i 的最短路长度 const dis = Array(n).fill(Infinity); dis[start] = 0; const vis = Array(n).fill(false); while (true) { let x = -1; for (let i = 0; i < n; i++) { if (!vis[i] && (x < 0 || dis[i] < dis[x])) { x = i; } } if (x < 0 || dis[x] === Infinity) { // 所有从起点能到达的点都被更新了 return -1; // 无法到达终点 } if (x === end) { // 找到终点,提前退出 return dis[x]; } vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度 for (let y = 0; y < n; y++) { dis[y] = Math.min(dis[y], dis[x] + g[x][y]); // 更新最短路长度 } } } }; /** * 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) */
javascript 解法, 执行用时: 204 ms, 内存消耗: 65 MB, 提交时间: 2024-03-26 09:33:42
var Graph = function(n, edges) { const g = Array.from({length: n}, () => []); // 邻接表 for (const [x, y, w] of edges) { g[x].push([y, w]); } this.addEdge = function(e) { g[e[0]].push([e[1], e[2]]); } this.shortestPath = function(start, end) { // dis[i] 表示从起点 start 出发,到节点 i 的最短路长度 const dis = Array(n).fill(Infinity); dis[start] = 0; const pq = new MinPriorityQueue({priority: (p) => p[0]}); pq.enqueue([0, start]); while (!pq.isEmpty()) { const [d, x] = pq.dequeue().element; if (x === end) { // 计算出从起点到终点的最短路长度 return d; } if (d > dis[x]) { // x 之前出堆过,无需更新邻居的最短路 continue; } for (const [y, w] of g[x]) { if (d + w < dis[y]) { dis[y] = d + w; // 更新最短路长度 pq.enqueue([dis[y], y]); } } } return -1; // 无法到达终点 } }; /** * 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) */
rust 解法, 执行用时: 29 ms, 内存消耗: 3.4 MB, 提交时间: 2024-03-26 09:33:20
use std::collections::BinaryHeap; struct Graph { g: Vec<Vec<(usize, i32)>>, // 邻接表 } impl Graph { fn new(n: i32, edges: Vec<Vec<i32>>) -> Self { let mut g = vec![vec![]; n as usize]; for e in &edges { g[e[0] as usize].push((e[1] as usize, e[2])); } Self { g } } fn add_edge(&mut self, e: Vec<i32>) { self.g[e[0] as usize].push((e[1] as usize, e[2])); } fn shortest_path(&self, node1: i32, node2: i32) -> i32 { let start = node1 as usize; let end = node2 as usize; // dis[i] 表示从起点 start 出发,到节点 i 的最短路长度 let mut dis = vec![i32::MAX; self.g.len()]; dis[start] = 0; let mut h = BinaryHeap::new(); h.push((0, start)); while let Some((d, x)) = h.pop() { let d = -d; if x == end { // 计算出从起点到终点的最短路长度 return d; } if d > dis[x] { // x 之前出堆过,无需更新邻居的最短路 continue; } for &(y, w) in &self.g[x] { if d + w < dis[y] { dis[y] = d + w; // 更新最短路长度 h.push((-dis[y], y)); // 加负号变最小堆 } } } -1 // 无法到达终点 } } /** * Your Graph object will be instantiated and called as such: * let obj = Graph::new(n, edges); * obj.add_edge(edge); * let ret_2: i32 = obj.shortest_path(node1, node2); */
rust 解法, 执行用时: 43 ms, 内存消耗: 3.1 MB, 提交时间: 2024-03-26 09:33:07
struct Graph { g: Vec<Vec<i32>>, // 邻接矩阵 } impl Graph { fn new(n: i32, edges: Vec<Vec<i32>>) -> Self { let n = n as usize; let mut g = vec![vec![i32::MAX / 2; n]; n]; for e in &edges { g[e[0] as usize][e[1] as usize] = e[2]; // 添加一条边(题目保证没有重边) } Self { g } } fn add_edge(&mut self, e: Vec<i32>) { self.g[e[0] as usize][e[1] as usize] = e[2]; // 添加一条边(题目保证这条边之前不存在) } fn shortest_path(&self, node1: i32, node2: i32) -> i32 { let n = self.g.len(); let start = node1 as usize; let end = node2 as usize; // dis[i] 表示从起点 start 出发,到节点 i 的最短路长度 let mut dis = vec![i32::MAX / 2; self.g.len()]; dis[start] = 0; let mut vis = vec![false; n]; loop { let mut x = n; for i in 0..n { if !vis[i] && (x == n || dis[i] < dis[x]) { x = i; } } if x == n || dis[x] == i32::MAX / 2 { // 所有从 start 能到达的点都被更新了 return -1; // 无法到达终点 } if x == end { // 找到终点,提前退出 return dis[x]; } vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度 for y in 0..n { dis[y] = dis[y].min(dis[x] + self.g[x][y]); // 更新最短路长度 } } } } /** * Your Graph object will be instantiated and called as such: * let obj = Graph::new(n, edges); * obj.add_edge(edge); * let ret_2: i32 = obj.shortest_path(node1, node2); */
java 解法, 执行用时: 64 ms, 内存消耗: 50.1 MB, 提交时间: 2023-04-17 17:00:08
class Graph { private static final int INF = Integer.MAX_VALUE / 3; // 防止更新最短路时加法溢出 private int[][] d; public Graph(int n, int[][] edges) { d = new int[n][n]; // 邻接矩阵(初始化为无穷大,表示 i 到 j 没有边) for (int i = 0; i < n; ++i) { Arrays.fill(d[i], INF); d[i][i] = 0; } for (var e : edges) d[e[0]][e[1]] = e[2]; // 添加一条边(输入保证没有重边和自环) for (int k = 0; k < n; ++k) for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]); } public void addEdge(int[] e) { int x = e[0], y = e[1], w = e[2], n = d.length; if (w >= d[x][y]) // 无需更新 return; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) d[i][j] = Math.min(d[i][j], d[i][x] + w + d[y][j]); } public int shortestPath(int start, int end) { int ans = d[start][end]; return ans < INF / 3 ? ans : -1; } } /** * Your Graph object will be instantiated and called as such: * Graph obj = new Graph(n, edges); * obj.addEdge(edge); * int param_2 = obj.shortestPath(node1,node2); */
python3 解法, 执行用时: 4056 ms, 内存消耗: 18.1 MB, 提交时间: 2023-04-17 16:59:50
class Graph: def __init__(self, n: int, edges: List[List[int]]): d = [[inf] * n for _ in range(n)] for i in range(n): d[i][i] = 0 for x, y, w in edges: d[x][y] = w # 添加一条边(输入保证没有重边和自环) for k in range(n): for i in range(n): for j in range(n): d[i][j] = min(d[i][j], d[i][k] + d[k][j]) self.d = d def addEdge(self, e: List[int]) -> None: d = self.d n = len(d) x, y, w = e if w >= d[x][y]: # 无需更新 return for i in range(n): for j in range(n): d[i][j] = min(d[i][j], d[i][x] + w + d[y][j]) def shortestPath(self, start: int, end: int) -> int: ans = self.d[start][end] return ans if ans < inf else -1 # Your Graph object will be instantiated and called as such: # obj = Graph(n, edges) # obj.addEdge(edge) # param_2 = obj.shortestPath(node1,node2)
golang 解法, 执行用时: 152 ms, 内存消耗: 7.2 MB, 提交时间: 2023-04-17 16:59:25
const inf = math.MaxInt / 3 // 防止更新最短路时加法溢出 type Graph [][]int func Constructor(n int, edges [][]int) Graph { d := make([][]int, n) // 邻接矩阵 for i := range d { d[i] = make([]int, n) for j := range d[i] { if j != i { d[i][j] = inf // 初始化为无穷大,表示 i 到 j 没有边 } } } for _, e := range edges { d[e[0]][e[1]] = e[2] // 添加一条边(输入保证没有重边和自环) } for k := range d { for i := range d { for j := range d { d[i][j] = min(d[i][j], d[i][k]+d[k][j]) } } } return d } func (d Graph) AddEdge(e []int) { x, y, w := e[0], e[1], e[2] if w >= d[x][y] { // 无需更新 return } for i := range d { for j := range d { d[i][j] = min(d[i][j], d[i][x]+w+d[y][j]) } } } func (d Graph) ShortestPath(start, end int) int { ans := d[start][end] if ans == inf { return -1 } return ans } func min(a, b int) int { if b < a { return b }; return a } /** * Your Graph object will be instantiated and called as such: * obj := Constructor(n, edges); * obj.AddEdge(edge); * param_2 := obj.ShortestPath(node1,node2); */
golang 解法, 执行用时: 112 ms, 内存消耗: 7.3 MB, 提交时间: 2023-04-17 16:58:43
const inf = math.MaxInt / 2 // 防止更新最短路时加法溢出 type Graph [][]int func Constructor(n int, edges [][]int) Graph { g := make([][]int, n) // 邻接矩阵 for i := range g { g[i] = make([]int, n) for j := range g[i] { g[i][j] = inf // 初始化为无穷大,表示 i 到 j 没有边 } } for _, e := range edges { g[e[0]][e[1]] = e[2] // 添加一条边(输入保证没有重边) } return g } func (g Graph) AddEdge(e []int) { g[e[0]][e[1]] = e[2] // 添加一条边(输入保证这条边之前不存在) } // 朴素 Dijkstra 算法 func (g Graph) ShortestPath(start, end int) int { n := len(g) dis := make([]int, n) // 从 start 出发,到各个点的最短路,如果不存在则为无穷大 for i := range dis { dis[i] = inf } dis[start] = 0 vis := make([]bool, n) for { // 找到当前最短路,去更新它的邻居的最短路, // 根据数学归纳法,dis[x] 一定是最短路长度 x := -1 for i, b := range vis { if !b && (x < 0 || dis[i] < dis[x]) { x = i } } if x < 0 || dis[x] == inf { // 所有从 start 能到达的点都被更新了 return -1 } if x == end { // 找到终点,提前退出 return dis[x] } vis[x] = true // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度 for y, w := range g[x] { dis[y] = min(dis[y], dis[x]+w) // 更新最短路长度 } } } func min(a, b int) int { if b < a { return b }; return a } /** * Your Graph object will be instantiated and called as such: * obj := Constructor(n, edges); * obj.AddEdge(edge); * param_2 := obj.ShortestPath(node1,node2); */
python3 解法, 执行用时: 1340 ms, 内存消耗: 17.7 MB, 提交时间: 2023-04-17 16:58:25
class Graph: def __init__(self, n: int, edges: List[List[int]]): g = [[inf] * n for _ in range(n)] # 邻接矩阵(初始化为无穷大,表示 i 到 j 没有边) for x, y, w in edges: g[x][y] = w # 添加一条边(输入保证没有重边) self.g = g def addEdge(self, e: List[int]) -> None: self.g[e[0]][e[1]] = e[2] # 添加一条边(输入保证这条边之前不存在) # 朴素 Dijkstra 算法 def shortestPath(self, start: int, end: int) -> int: n = len(self.g) dis = [inf] * n # 从 start 出发,到各个点的最短路,如果不存在则为无穷大 dis[start] = 0 vis = [False] * n while True: # 至多循环 n 次 # 找到当前最短路,去更新它的邻居的最短路 # 根据数学归纳法,dis[x] 一定是最短路长度 x = -1 for i, (b, d) in enumerate(zip(vis, dis)): if not b and (x < 0 or d < dis[x]): x = i if x < 0 or dis[x] == inf: # 所有从 start 能到达的点都被更新了 return -1 if x == end: # 找到终点,提前退出 return dis[x] vis[x] = True # 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度 for y, w in enumerate(self.g[x]): if dis[x] + w < dis[y]: dis[y] = dis[x] + w # 更新最短路长度 # Your Graph object will be instantiated and called as such: # obj = Graph(n, edges) # obj.addEdge(edge) # param_2 = obj.shortestPath(node1,node2)
java 解法, 执行用时: 72 ms, 内存消耗: 49.6 MB, 提交时间: 2023-04-17 16:58:11
class Graph { private static final int INF = Integer.MAX_VALUE / 2; // 防止更新最短路时加法溢出 private int[][] g; public Graph(int n, int[][] edges) { g = new int[n][n]; // 邻接矩阵(初始化为无穷大,表示 i 到 j 没有边) for (int i = 0; i < n; ++i) Arrays.fill(g[i], INF); for (var e : edges) g[e[0]][e[1]] = e[2]; // 添加一条边(输入保证没有重边) } public void addEdge(int[] e) { g[e[0]][e[1]] = e[2]; // 添加一条边(输入保证这条边之前不存在) } // 朴素 Dijkstra 算法 public int shortestPath(int start, int end) { int n = g.length; var dis = new int[n]; // 从 start 出发,到各个点的最短路,如果不存在则为无穷大 Arrays.fill(dis, INF); dis[start] = 0; var vis = new boolean[n]; for (;;) { // 找到当前最短路,去更新它的邻居的最短路 // 根据数学归纳法,dis[x] 一定是最短路长度 int x = -1; for (int i = 0; i < n; ++i) if (!vis[i] && (x < 0 || dis[i] < dis[x])) x = i; if (x < 0 || dis[x] == INF) // 所有从 start 能到达的点都被更新了 return -1; if (x == end) // 找到终点,提前退出 return dis[x]; vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度 for (int y = 0; y < n; ++y) dis[y] = Math.min(dis[y], dis[x] + g[x][y]); // 更新最短路长度 } } } /** * Your Graph object will be instantiated and called as such: * Graph obj = new Graph(n, edges); * obj.addEdge(edge); * int param_2 = obj.shortestPath(node1,node2); */
cpp 解法, 执行用时: 268 ms, 内存消耗: 62 MB, 提交时间: 2023-04-17 16:57:57
class Graph { vector<vector<int>> g; public: Graph(int n, vector<vector<int>> &edges) { // 邻接矩阵(初始化为无穷大,表示 i 到 j 没有边) g = vector<vector<int>>(n, vector<int>(n, INT_MAX / 2)); for (auto &e: edges) g[e[0]][e[1]] = e[2]; // 添加一条边(输入保证没有重边) } void addEdge(vector<int> e) { g[e[0]][e[1]] = e[2]; // 添加一条边(输入保证这条边之前不存在) } // 朴素 Dijkstra 算法 int shortestPath(int start, int end) { int n = g.size(); vector<int> dis(n, INT_MAX / 2), vis(n); dis[start] = 0; for (;;) { // 找到当前最短路,去更新它的邻居的最短路 // 根据数学归纳法,dis[x] 一定是最短路长度 int x = -1; for (int i = 0; i < n; ++i) if (!vis[i] && (x < 0 || dis[i] < dis[x])) x = i; if (x < 0 || dis[x] == INT_MAX / 2) // 所有从 start 能到达的点都被更新了 return -1; if (x == end) // 找到终点,提前退出 return dis[x]; vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度 for (int y = 0; y < n; ++y) dis[y] = min(dis[y], dis[x] + g[x][y]); // 更新最短路长度 } } }; /** * Your Graph object will be instantiated and called as such: * Graph* obj = new Graph(n, edges); * obj->addEdge(edge); * int param_2 = obj->shortestPath(node1,node2); */