代码结构概览
这是一个实现 Dijkstra算法 的无向图最短路径程序,核心逻辑分为:
- 邻接矩阵初始化(用
0x3f3f
表示无穷大) - Dijkstra算法核心流程
- 输入/输出处理
关键变量解析
static int N = 1010; // 最大节点数
static int[][] g = new int[N][N]; // 邻接矩阵(存储边权)
static int[] dist = new int[N]; // 起点到各点的最短距离
static boolean[] st = new boolean[N]; // 标记节点是否已确定最短路径
算法流程详解
1. 邻接矩阵初始化
for (int i = 1; i <= n; i++) {
Arrays.fill(g[i], 0x3f3f); // 初始化为无穷大
}
0x3f3f
的作用:表示两个节点之间没有直接连边(即初始距离为无穷大)。- 为何选择
0x3f3f
:- 值足够大(16191),避免被实际边权覆盖。
- 方便判断不可达情况(如
dist[n] == 0x3f3f
)。
2. 处理输入边
for (int i = 0; i < m; i++) {
int u = in.nextInt();
int v = in.nextInt();
int c = in.nextInt();
g[u][v] = Math.min(g[u][v], c); // 处理重边,保留最小边权
g[v][u] = Math.min(g[v][u], c); // 无向图双向更新
}
- 处理重边:若存在多条相同边,保留权重最小的。
- 无向图处理:同时更新
g[u][v]
和g[v][u]
,确保双向连通性。
3. Dijkstra算法核心
初始化距离数组
Arrays.fill(dist, 0x3f3f); // 所有节点初始距离为无穷大
dist[1] = 0; // 起点到自身距离为0
迭代寻找最短路径
for (int i = 0; i < n; i++) { // 迭代n次(每次确定一个节点的最短路径)
int t = -1;
// 找到未确定最短路径的节点中距离最小的
for (int j = 1; j <= n; j++) {
if (!st[j] && (t == -1 || dist[t] > dist[j])) {
t = j;
}
}
st[t] = true; // 标记该节点已确定最短路径
// 通过节点t更新其他节点的距离
for (int j = 1; j <= n; j++) {
dist[j] = Math.min(dist[j], dist[t] + g[t][j]);
}
}
- 贪心策略:每次选择未访问节点中距离最小的节点
t
,认为此时dist[t]
是最终最短距离。 - 松弛操作:通过
t
更新其他节点的距离(若经过t
更短)。
结果判断
if (dist[n] == 0x3f3f) return -1; // 终点不可达
return dist[n];
关键问题解答
1. 为何用 0x3f3f
而非常见的 0x3f3f3f3f
?
- 适用场景:假设边权较小(如
c ≤ 1000
),0x3f3f
(16191)足够大,不会溢出。 - 节省空间:使用较小的值可减少内存占用(但需根据题目数据范围调整)。
2. 为何迭代 n
次?
- Dijkstra算法要求每次确定一个节点的最短距离,共需
n
次迭代。
3. 如何处理无向图?
- 输入时同时更新
g[u][v]
和g[v][u]
,确保双向连通性。
潜在问题及优化
-
邻接矩阵空间浪费
- 若节点数
n
接近N=1010
,空间复杂度为 O(N2)O(N2),可能超出内存限制。 - 优化方案:改用邻接表存储稀疏图。
- 若节点数
-
无穷大值选择
- 若边权较大(如
c > 10000
),0x3f3f
可能不足,导致错误。 - 推荐调整:使用
0x3f3f3f3f
(十进制 1061109567)。
- 若边权较大(如
总结
- 代码功能:通过 Dijkstra 算法求解无向图中起点(节点1)到终点(节点n)的最短路径。
- 核心逻辑:贪心选择最近节点 + 松弛操作更新距离。
- 注意事项:确保无穷大值足够大,处理无向图的双向边。
实际应用中需根据题目数据范围调整 N
和 0x3f3f
的值。
完整代码
package Lanqiao;
import java.util.Arrays;
import java.util.Scanner;
/**
* @author zb
* date2025/4/1 16:02
*/
public class Acwing849 {
static int N = 1010;
static int n, m ;
// 任意两点的距离
static int [][]g = new int[N][N];
// 第i个点到起点的最短路径
static int[] dist =new int[N];
// 判断某个点是否被访问过
static boolean [] st = new boolean[N];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n =in.nextInt();m =in.nextInt();
for (int i = 1; i <=n ; i++) {
Arrays.fill(g[i],0x3f3f);
}
for (int i = 0; i <m ; i++) {
int u = in.nextInt();
int v= in.nextInt();
int c = in.nextInt();
g[u][v] =Math.min(g[u][v],c);
g[v][u] =Math.min(g[v][u],c);
}
System.out.println(dijkstra());
in.close();
}
static int dijkstra(){
Arrays.fill(dist,0x3f3f);
dist[1] =0;
// 迭代n次,迭代之后获取最终的结果集合
for (int i = 0; i <n ; i++) {
int t = -1; //代表还没有最短的点
for (int j = 1; j <=n ; j++) {
if(!st[j]&&(t==-1||dist[t]>dist[j])){
t =j ;
}
}
// 表示这个点被访问过
st[t] = true;
for (int j = 1; j <=n ; j++) {
// 从t点中转到j点的距离
dist[j] = Math.min(dist[j],dist[t]+g[t][j]);
}
}
if(dist[n]==0x3f3f)return -1;
return dist[n];
}
}