[Python3/Java/C++/Go/TypeScript] 一题一解:Dijkstra 算法(清晰题解)
方法一:Dijkstra 算法
我们可以按照题目描述,构造一个有向图 $g$,其中每条边 $(u, v)$ 有两种走法:
- 直接走,花费 $w$,对应边 $(u, v)$。
- 反转走,花费 $2w$,对应边 $(v, u)$。
然后我们可以使用 Dijkstra 算法在图 $G$ 上求解从节点 $0$ 到节点 $n-1$ 的最短路径,即为所求的最小总成本。
具体地,我们定义一个优先队列 $pq$,其中每个元素为一个二元组 $(d, u)$,表示当前到达节点 $u$ 的最小花费为 $d$。我们还定义一个数组 $\textit{dist}$,其中 $\textit{dist}[u]$ 表示从节点 $0$ 到节点 $u$ 的最小花费。初始时,我们将 $\textit{dist}[0] = 0$,其他节点的花费均设为无穷大,并将 $(0, 0)$ 入队。
在每次迭代中,我们从优先队列中取出花费最小的节点 $(d, u)$,如果 $d$ 大于 $\textit{dist}[u]$,则跳过该节点。否则,我们遍历节点 $u$ 的所有邻居节点 $v$,计算通过节点 $u$ 到达节点 $v$ 的新花费 $nd = d + w$,如果 $nd$ 小于 $\textit{dist}[v]$,则更新 $\textit{dist}[v] = nd$ 并将 $(nd, v)$ 入队。
当我们取出节点 $n-1$ 时,此时的 $d$ 即为从节点 $0$ 到节点 $n-1$ 的最小总成本。如果优先队列为空且未取出节点 $n-1$,则说明无法到达节点 $n-1$,返回 -1。
###python
class Solution:
def minCost(self, n: int, edges: List[List[int]]) -> int:
g = [[] for _ in range(n)]
for u, v, w in edges:
g[u].append((v, w))
g[v].append((u, w * 2))
pq = [(0, 0)]
dist = [inf] * n
dist[0] = 0
while pq:
d, u = heappop(pq)
if d > dist[u]:
continue
if u == n - 1:
return d
for v, w in g[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heappush(pq, (nd, v))
return -1
###java
class Solution {
public int minCost(int n, int[][] edges) {
List<int[]>[] g = new ArrayList[n];
Arrays.setAll(g, k -> new ArrayList<>());
for (int[] e : edges) {
int u = e[0], v = e[1], w = e[2];
g[u].add(new int[] {v, w});
g[v].add(new int[] {u, w * 2});
}
final int inf = Integer.MAX_VALUE / 2;
int[] dist = new int[n];
Arrays.fill(dist, inf);
dist[0] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
pq.offer(new int[] {0, 0});
while (!pq.isEmpty()) {
int[] cur = pq.poll();
int d = cur[0], u = cur[1];
if (d > dist[u]) {
continue;
}
if (u == n - 1) {
return d;
}
for (int[] nei : g[u]) {
int v = nei[0], w = nei[1];
int nd = d + w;
if (nd < dist[v]) {
dist[v] = nd;
pq.offer(new int[] {nd, v});
}
}
}
return -1;
}
}
###cpp
class Solution {
public:
int minCost(int n, vector<vector<int>>& edges) {
using pii = pair<int, int>;
vector<vector<pii>> g(n);
for (auto& e : edges) {
int u = e[0], v = e[1], w = e[2];
g[u].push_back({v, w});
g[v].push_back({u, w * 2});
}
const int inf = INT_MAX / 2;
vector<int> dist(n, inf);
dist[0] = 0;
priority_queue<pii, vector<pii>, greater<pii>> pq;
pq.push({0, 0});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) {
continue;
}
if (u == n - 1) {
return d;
}
for (auto& [v, w] : g[u]) {
int nd = d + w;
if (nd < dist[v]) {
dist[v] = nd;
pq.push({nd, v});
}
}
}
return -1;
}
};
###go
func minCost(n int, edges [][]int) int {
g := make([][][2]int, n)
for _, e := range edges {
u, v, w := e[0], e[1], e[2]
g[u] = append(g[u], [2]int{v, w})
g[v] = append(g[v], [2]int{u, w * 2})
}
inf := math.MaxInt / 2
dist := make([]int, n)
for i := range dist {
dist[i] = inf
}
dist[0] = 0
pq := &hp{}
heap.Init(pq)
heap.Push(pq, pair{0, 0})
for pq.Len() > 0 {
cur := heap.Pop(pq).(pair)
d, u := cur.x, cur.i
if d > dist[u] {
continue
}
if u == n-1 {
return d
}
for _, ne := range g[u] {
v, w := ne[0], ne[1]
if nd := d + w; nd < dist[v] {
dist[v] = nd
heap.Push(pq, pair{nd, v})
}
}
}
return -1
}
type pair struct{ x, i int }
type hp []pair
func (h hp) Len() int { return len(h) }
func (h hp) Less(i, j int) bool { return h[i].x < h[j].x }
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(x any) { *h = append(*h, x.(pair)) }
func (h *hp) Pop() (x any) {
a := *h
x = a[len(a)-1]
*h = a[:len(a)-1]
return
}
###ts
function minCost(n: number, edges: number[][]): number {
const g: number[][][] = Array.from({ length: n }, () => []);
for (const [u, v, w] of edges) {
g[u].push([v, w]);
g[v].push([u, w * 2]);
}
const dist: number[] = Array(n).fill(Infinity);
dist[0] = 0;
const pq = new PriorityQueue<number[]>((a, b) => a[0] - b[0]);
pq.enqueue([0, 0]);
while (!pq.isEmpty()) {
const [d, u] = pq.dequeue();
if (d > dist[u]) {
continue;
}
if (u === n - 1) {
return d;
}
for (const [v, w] of g[u]) {
const nd = d + w;
if (nd < dist[v]) {
dist[v] = nd;
pq.enqueue([nd, v]);
}
}
}
return -1;
}
时间复杂度 $O(n + m \times \log m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别为节点数和边数。
有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

