Java 数据结构 - 最短路径
Java 实现图的最短路径算法
1. 引言
在图论中,最短路径问题是一个经典且重要的问题。它在网络路由、导航系统、社交网络分析等多个领域都有广泛应用。本文将介绍两种常用的最短路径算法:Dijkstra 算法和 Floyd-Warshall 算法,并提供 Java 实现。
2. 最短路径问题概述
最短路径问题是指在一个加权图中找到从一个顶点到另一个顶点的路径,使得路径上的权重之和最小。根据问题的具体要求,我们可以分为:
- 单源最短路径:从一个源顶点到所有其他顶点的最短路径。
- 所有顶点对之间的最短路径:任意两个顶点之间的最短路径。
3. Dijkstra 算法
Dijkstra 算法用于解决单源最短路径问题,适用于边权重为非负数的情况。
3.1 算法步骤
- 初始化距离数组,将源顶点距离设为 0,其他顶点设为无穷大。
- 选择当前未访问的距离最小的顶点。
- 更新该顶点的邻接顶点的距离。
- 重复步骤 2 和 3,直到所有顶点都被访问。
3.2 Java 实现
import java.util.*;
public class Dijkstra {
private int V; // 顶点数
private List<List<Node>> adj; // 邻接表
class Node implements Comparable<Node> {
int vertex, weight;
Node(int v, int w) {
vertex = v;
weight = w;
}
public int compareTo(Node other) {
return Integer.compare(this.weight, other.weight);
}
}
public Dijkstra(int v) {
V = v;
adj = new ArrayList<>(V);
for (int i = 0; i < V; i++) {
adj.add(new ArrayList<>());
}
}
// 添加边
public void addEdge(int u, int v, int weight) {
adj.get(u).add(new Node(v, weight));
adj.get(v).add(new Node(u, weight)); // 如果是有向图,则删除此行
}
// Dijkstra 算法实现
public int[] shortestPath(int src) {
int[] dist = new int[V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
PriorityQueue<Node> pq = new PriorityQueue<>();
pq.add(new Node(src, 0));
while (!pq.isEmpty()) {
int u = pq.poll().vertex;
for (Node v : adj.get(u)) {
if (dist[u] + v.weight < dist[v.vertex]) {
dist[v.vertex] = dist[u] + v.weight;
pq.add(new Node(v.vertex, dist[v.vertex]));
}
}
}
return dist;
}
public static void main(String[] args) {
Dijkstra g = new Dijkstra(5);
g.addEdge(0, 1, 9);
g.addEdge(0, 2, 6);
g.addEdge(0, 3, 5);
g.addEdge(0, 4, 3);
g.addEdge(2, 1, 2);
g.addEdge(2, 3, 4);
int[] dist = g.shortestPath(0);
System.out.println("从顶点 0 到其他顶点的最短距离:");
for (int i = 0; i < dist.length; i++) {
System.out.println(i + ": " + dist[i]);
}
}
}
4. Floyd-Warshall 算法
Floyd-Warshall 算法用于解决所有顶点对之间的最短路径问题。
4.1 算法步骤
- 初始化距离矩阵,直接相连的顶点之间填入边的权重,其他填入无穷大。
- 对于每个中间顶点 k,更新任意两个顶点 i 和 j 之间的距离:
如果 dist[i][k] + dist[k][j] < dist[i][j],则更新 dist[i][j]。 - 重复步骤 2,直到所有顶点都作为中间顶点被考虑过。
4.2 Java 实现
public class FloydWarshall {
private static final int INF = 99999;
private int V; // 顶点数
private int[][] dist; // 距离矩阵
public FloydWarshall(int v) {
V = v;
dist = new int[V][V];
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (i == j) {
dist[i][j] = 0;
} else {
dist[i][j] = INF;
}
}
}
}
// 添加边
public void addEdge(int u, int v, int weight) {
dist[u][v] = weight;
// 如果是无向图,则还需要 dist[v][u] = weight
}
// Floyd-Warshall 算法实现
public void floydWarshall() {
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
}
// 打印结果
public void printSolution() {
System.out.println("所有顶点对之间的最短距离:");
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i][j] == INF) {
System.out.print("INF ");
} else {
System.out.print(dist[i][j] + " ");
}
}
System.out.println();
}
}
public static void main(String[] args) {
FloydWarshall fw = new FloydWarshall(4);
fw.addEdge(0, 1, 5);
fw.addEdge(0, 3, 10);
fw.addEdge(1, 2, 3);
fw.addEdge(2, 3, 1);
fw.floydWarshall();
fw.printSolution();
}
}
5. 算法比较
特性 | Dijkstra | Floyd-Warshall |
---|---|---|
时间复杂度 | O((V+E)logV) | O(V^3) |
空间复杂度 | O(V) | O(V^2) |
适用场景 | 单源最短路径 | 所有顶点对最短路径 |
负权边 | 不支持 | 支持(无负权环) |
稀疏图效率 | 较高 | 较低 |
6. 应用场景
- 网络路由:计算网络中数据包的最佳路径。
- 地图导航:寻找两地之间的最短或最快路线。
- 社交网络分析:计算用户之间的最短连接路径。
- 机器学习:在某些图形模型中用于优化。
7. 总结
最短路径算法是图论中的基础算法,在实际应用中有着广泛的用途。Dijkstra 算法适用于单源最短路径问题,而 Floyd-Warshall 算法则可以解决所有顶点对之间的最短路径问题。选择哪种算法取决于具体的问题需求和图的特性。在实际应用中,还需要考虑图的规模、稀疏程度等因素来选择合适的算法和优化策略。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?