Dijkstra 算法
- dijkstra 算法可以用来解决单源最短路径问题
- dijkstra算法不能求带负权边的最短路径
- 有向图或无向图均可
原理
它的核心思想是贪心策略:每次从 “未确定最短路径” 的顶点中选择距离起点最近的顶点,确定其最短路径后,再以该顶点为中介,更新其他顶点的距离。
-
核心思想:
维护两个关键集合:- 已确定最短路径的顶点集(S):这些顶点到起点的最短距离已最终确定,不会再被修改。
- 未确定最短路径的顶点集(U):这些顶点到起点的距离是当前已知的临时值,可能会被更新。
算法通过以下步骤迭代:
① 从 U 中选择距离起点最近的顶点u,将其加入 S;
② 以u为中介点,更新 U 中所有与u相邻的顶点v的距离(若u→v的路径比当前v的距离更短,则更新);
③ 重复①②,直到所有顶点都加入 S。 -
为什么能处理非负权边?
由于边权非负,当一个顶点u被加入 S 时,其到起点的距离已经是最短的 —— 因为任何后续路径(经过其他未访问顶点)的总权值只会更大(非负累加),不可能比当前距离更短。
通用模版(邻接矩阵)
Dijkstra() {
初始化;
for(循环n次) {
u = 使dis[u]最小的还未被访问的顶点的编号;
记u为确定值;
for(从u出发能到达的所有顶点v){
if(v未被访问 && 以u为中介点使s到顶点v的最短距离更优)
优化dis[v];
}
}
}
实现步骤(详细流程)
假设图有n个顶点,起点为start,用dis[i]表示起点到顶点i的当前最短距离,visited[i]标记顶点i是否已加入集合 S。
步骤 1:初始化
- 距离数组:
dis[i] = 无穷大(INF)(表示初始时无法到达),dis[start] = 0(起点到自身距离为 0)。 - 访问标记:
visited[i] = false(所有顶点初始都在 U 中)。
步骤 2:迭代确定最短路径(共 n 次,每次确定一个顶点)
- 选择当前最短距离的顶点:
从 U(visited[i] = false)中找到dis[i]最小的顶点u,作为本次要加入 S 的顶点。 - 标记为已确定:
设visited[u] = true(将u加入 S)。 - 松弛操作(更新相邻顶点距离):
对所有与u相邻的顶点v(即存在边u→v),若v仍在 U 中(visited[v] = false),则检查:
若dis[v] > dis[u] + 边u→v的权重,则更新dis[v] = dis[u] + 边u→v的权重(表示通过u到v的路径更短)。
步骤 3:结束
当所有顶点都被加入 S(visited[i]全为true),dis数组中存储的就是起点到各顶点的最短距离。
代码实现(C++)
- 基础实现(邻接矩阵,时间复杂度 O (n²))
适用于顶点数较少(n≤1000)的稠密图。
- 优化实现(邻接表 + 优先队列,时间复杂度 O (m log n))
适用于顶点数多(n>1000)的稀疏图(边数少),用优先队列快速找到 “当前最短距离顶点”。
#include <iostream>
#include <vector>
#include <queue> // 优先队列
#include <climits>
using namespace std;
const int INF = INT_MAX;
// 邻接表:每个顶点存储(相邻顶点, 边权重)
vector<int> dijkstra(int n, int start, const vector<vector<pair<int, int>>>& adj) {
vector<int> dis(n, INF); // 距离数组
dis[start] = 0;
// 优先队列(小根堆):存储(距离, 顶点),按距离升序排列
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.push({0, start}); // 起点入队
while (!pq.empty()) {
// 步骤1:取出当前距离最小的顶点u
int u = pq.top().second;
int current_dist = pq.top().first;
pq.pop();
// 若当前距离大于已知最短距离,说明u已被处理过,跳过
if (current_dist > dis[u]) continue;
// 步骤2:松弛u的所有相邻顶点
for (const auto& edge : adj[u]) {
int v = edge.first; // 相邻顶点
int weight = edge.second; // 边权重
// 若通过u到v的路径更短
if (dis[v] > dis[u] + weight) {
dis[v] = dis[u] + weight;
pq.push({dis[v], v}); // 将新距离入队
}
}
}
return dis;
}
int main() {
// 示例图(邻接表)
int n = 4;
vector<vector<pair<int, int>>> adj(n);
// 无向图:添加双向边
adj[0].push_back({1, 2});
adj[1].push_back({0, 2});
adj[0].push_back({2, 5});
adj[2].push_back({0, 5});
adj[1].push_back({2, 1});
adj[2].push_back({1, 1});
adj[1].push_back({3, 4});
adj[3].push_back({1, 4});
adj[2].push_back({3, 1});
adj[3].push_back({2, 1});
int start = 0;
vector<int> shortest = dijkstra(n, start, adj);
// 输出结果
cout << "起点" << start << "到各顶点的最短距离:" << endl;
for (int i = 0; i < n; i++) {
if (shortest[i] == INF) {
cout << "到" << i << ":不可达" << endl;
} else {
cout << "到" << i << ":" << shortest[i] << endl;
}
}
return 0;
}
1万+

被折叠的 条评论
为什么被折叠?



