迪杰斯特拉算法(Dijkstra’s algorithm)是一种用于解决单源最短路径问题的贪心算法。它可以找到从一个起始顶点到其他所有顶点的最短路径,并且适用于边的权重非负的图。
算法步骤如下:
本质上是维护两个集合,S 集合表示已处理的点,U 集合表示未处理的点。每次把一个点从 U 放到 S 里,同时要更新 S 中的点到 U 中的点的距离。直到 U 中没有点。
Dijkstra 算法的关键点在于每次选择距离最小的顶点并更新与该顶点相邻的顶点的距离,这样逐步扩展最短路径。算法的正确性基于贪心策略:每次找到距离最小的顶点,该顶点的最短路径已经确定,不再需要重新访问。
需要注意的是,Dijkstra 算法要求边的权重非负。如果存在负权边,应该使用 Bellman-Ford 算法来解决单源最短路径问题。
vector<int> dijkstra(const vector<vector<int>>& graph, int start, unordered_map<int, int>& pre) {
int n = graph.size();
vector<int> dist(n, INT_MAX);
vector<bool> visited(n, false);
dist[start] = 0;
for (int i = 0; i < n - 1; ++i) {
int u = -1;
// 找到起点到未访问的节点中距离最短的点
for (int j = 0; j < n; ++j) {
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j;
}
}
visited[u] = true;
// 更新起点到所有的点的距离,判断从点 u 到其它的点是否会更近
for (int v = 0; v < n; ++v) {
if (graph[u][v] != 0 && !visited[v]) {
if (dist[v] > dist[u] + graph[u][v]) {
dist[v] = dist[u] + graph[u][v];
pre[v] = u; // 记录路径
}
}
}
}
return dist;
}
// 获取起始节点到某个节点的路径
vector<int> getPath(int start, int end, const unordered_map<int, int>& predecessors) {
vector<int> path;
int current = end;
while (current != start) {
path.push_back(current);
current = predecessors.at(current);
}
path.push_back(start);
reverse(path.begin(), path.end());
return path;
}
测试
int main() {
vector<vector<int>> graph = {
{0, 10, 3, 0},
{10, 0, 0, 2},
{3, 0, 0, 7},
{0, 2, 7, 0}
};
int start = 0;
unordered_map<int, int> pre;
vector<int> shortestDist = dijkstra(graph, start, pre);
// 起始节点到其它节点的最短路径大小
for (unsigned i = 0; i < shortestDist.size(); ++i) {
cout << "Vertex " << i << ": " << shortestDist[i] << endl;
}
// 打印起始节点到节点 3 的具体路径
vector<int> path = getPath(start, 3, pre);
for (auto it : path) {
cout << it << " ";
}
cout << endl;
return 0;
}
测试例子中表示的图为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VM2UQcfh-1690272631336)(/wp-content/uploads/2023/07/Snipaste_2023-07-25_15-49-56-300x158.png)]