Dijkstra算法是解决单源最短路径问题的经典算法。
一、算法核心思想
Dijkstra算法的核心是**贪心策略**和**动态规划**的结合:
-
**贪心选择**:每次选择当前距离源点最近的未访问节点
-
**最优子结构**:最短路径的子路径也是最短路径
-
**松弛操作**:通过新加入节点更新其他节点的距离估计
```mermaid
graph TD
A[初始化] --> B[选择最近节点]
B --> C{所有节点已处理?}
C -->|否| D[松弛操作更新邻居]
D --> B
C -->|是| E[输出结果]
三、算法精髓详解
- **贪心选择的正确性证明**:
-
设当前未访问节点中距离最小者为v
-
假设存在更短路径:源点→...→w→v(w未访问)
-
但dist[w] ≥ dist[v](因为v
是最小值),矛盾!
- ∴ v的最短路径已确定
- **松弛操作的本质**:
```math
\text{dist}[j] = \min(\text{dist}[j], \text{dist}[v] + w(v,j))
```
-
这是算法中的**状态转移方程**
-
基于三角不等式:源点到j的距离 ≤ 源点到v的距离 + v到j的距离
- **前驱数组的妙用**:
-
存储路径信息:`p[j] = v` 表示在最短路径上,j的前驱是v
-
路径重构:递归回溯 `p[j] → p[v] → ... → 源点`
代码示例:
输入图中节点个数和节点之间的边数,然后根据提示输入不同节点之间每个边的起点和终点以及两点之间的距离,然后输入源点,使用dijkstra算法求解并打印出输入源点到不同节点的最短距离以及路径:
cpp
#include <iostream>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 1005;
const int INF = 0x3FFFFFFF; // 更合适的无穷大值
int G[N][N], dist[N];
int p[N];
int n, m;
bool flag[N];
void dijkstra(int u)
{
// 初始化
for (int i = 1; i <= n; ++i) {
dist[i] = G[u][i];
flag[i] = false;
if (dist[i] == INF) {
p[i] = -1;
} else {
p[i] = u;
}
}
dist[u] = 0;
flag[u] = true;
// 主循环
for (int i = 1; i < n; ++i) {
int minDist = INF, minNode = -1;
// 在未访问节点中寻找距离最小的节点
for (int j = 1; j <= n; ++j) {
if (!flag[j] && dist[j] < minDist) {
minDist = dist[j];
minNode = j;
}
}
// 所有节点已访问或剩余节点不可达
if (minNode == -1) return;
flag[minNode] = true;
// 松弛操作:更新相邻节点的最短距离
for (int j = 1; j <= n; ++j) {
if (!flag[j] && G[minNode][j] != INF &&
dist[j] > dist[minNode] + G[minNode][j]) {
dist[j] = dist[minNode] + G[minNode][j];
p[j] = minNode;
}
}
}
}
// 递归打印从源点到目标节点的路径
void printPath(int target)
{
if (target == -1) return;
printPath(p[target]);
// cout << target;
// if(target != p[target]) cout << " -> ";
cout << target << " -> ";;
}
int main()
{
// 初始化邻接矩阵
fill(G[0], G[0] + N*N, INF);
cout << "输入节点数n和边数m: ";
cin >> n >> m;
cout << "输入" << m << "条边(起点 终点 权重):" << endl;
for (int i = 0; i < m; ++i) {
int u, v, w;
cin >> u >> v >> w;
G[u][v] = w;
// 无向图添加反向边(如果需要)
// G[v][u] = w;
}
int source;
cout << "输入源点: ";
cin >> source;
dijkstra(source);
cout << "\n源点 " << source << " 到各节点的最短距离:" << endl;
for (int i = 1; i <= n; ++i) {
cout << "节点 " << i << ": ";
if (dist[i] == INF) {
cout << "不可达";
} else {
cout << dist[i] << "\t路径: ";
printPath(i);
}
cout << endl;
}
return 0;
}
测试:
// 输入节点数n和边数m: 5 7
// 输入7条边(起点 终点 权重):
// 1 2 4
// 1 3 1
// 2 3 2
// 2 4 5
// 3 2 1
// 3 4 8
// 4 5 3
// 输入源点: 1
// 源点 1 到各节点的最短距离:
// 节点 1: 0 路径: 1 ->
// 节点 2: 2 路径: 1 -> 3 -> 2 ->
// 节点 3: 1 路径: 1 -> 3 ->
// 节点 4: 7 路径: 1 -> 3 -> 2 -> 4 ->
// 节点 5: 10 路径: 1 -> 3 -> 2 -> 4 -> 5 ->
