📅  最后修改于: 2023-12-03 14:40:43.805000             🧑  作者: Mango
Dijkstra 和 Floyd-Warshall 算法都是最短路径算法,但它们的具体实现方式和适用场景有所不同。
Dijkstra 算法采用贪心策略,从起点开始,每次选择当前距离起点最近的一个顶点,然后以该顶点为中心,更新与它直接相连的顶点的距离。不断重复这个过程,直到所有顶点的最短距离都被计算出来。
Dijkstra 算法对于稠密图的效率比较高,因为它只计算与当前顶点相邻的顶点,而不会对所有的顶点都进行计算。
Dijkstra 算法只能处理边权非负的图,否则会出现负权回路的情况。此外,每次选择最近的顶点后,需要更新与它直接相连的所有顶点,这个过程比较耗时,因此在稀疏图中比较慢。
// 给定起点 s 和图 G,返回起点到所有顶点的最短路径
vector<int> dijkstra(int s, vector<vector<pair<int, int>>>& G) {
int n = G.size();
vector<int> dist(n, INT_MAX / 2); // 到每个顶点的最短距离
vector<bool> used(n, false); // 是否已经访问过
dist[s] = 0;
while (true) {
int v = -1;
for (int u = 0; u < n; ++u) {
if (!used[u] && (v == -1 || dist[u] < dist[v])) {
v = u;
}
}
if (v == -1) {
break;
}
used[v] = true;
for (auto e : G[v]) {
int u = e.first;
int w = e.second;
if (dist[v] + w < dist[u]) {
dist[u] = dist[v] + w;
}
}
}
return dist;
}
Floyd-Warshall 算法采用动态规划的思想,它的核心思想是利用中间节点的信息来更新起点到终点的距离。对于每一对顶点 u 和 v,算法维护一个矩阵 D,其中 D[u][v] 表示从 u 到 v 的最短距离。算法的主要思想是,对于任意的中间节点 k,如果从 u 到 v 的距离要经过节点 k,那么可以在已知的 D[u][k] 和 D[k][v] 基础上计算出 D[u][v]。
Floyd-Warshall 算法可以处理边权为负的图,而且比较适合稠密图,因为它的复杂度为 O(n^3)。
Floyd-Warshall 算法的时间复杂度为 O(n^3),在稀疏图中比较慢,此外,它需要维护一个矩阵,空间复杂度也比较高。
// 计算任意两个顶点之间的最短距离
vector<vector<int>> floyd_warshall(vector<vector<int>>& G) {
int n = G.size();
vector<vector<int>> dist(n, vector<int>(n, INT_MAX / 2));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i == j) {
dist[i][j] = 0; // 自己到自己的距离为 0
} else if (G[i][j] != -1) { // 有边相连
dist[i][j] = G[i][j];
}
}
}
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (dist[i][k] != INT_MAX / 2 && dist[k][j] != INT_MAX / 2) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
return dist;
}
Dijkstra 算法适合稠密图或者边权非负的情况,而 Floyd-Warshall 算法适合稠密图或者边权带负数的情况。对于稀疏图,可以采用 Bellman-Ford 算法来求解最短路径。