Dijkstra 算法详解
Dijkstra 算法用于计算加权图中从源节点到所有其他节点的最短路径。算法的步骤和原理可以分解如下:
1. 数据结构
- 图的表示:可以使用邻接矩阵或邻接表来表示图。邻接表通常更节省空间,尤其是在稀疏图中。
- 距离数组:一个数组
dist
,其中dist[i]
表示从起点到节点i
的最短距离。 - 优先队列:使用优先队列(通常用最小堆实现)来高效地获取当前距离最小的节点。
2. 初始化
- 将起点的距离设为 0,其他所有节点的距离设为 ∞(无穷大)。
- 将起点加入优先队列。
3. 迭代过程
- 当优先队列不为空时:
- 从队列中取出距离最小的节点
u
。 - 对于每个与
u
相邻的节点v
,检查通过u
到达v
的路径是否更短。 - 如果更短,则更新
dist[v]
,并将v
加入优先队列。
- 从队列中取出距离最小的节点
4. 结束条件
- 当所有节点的最短路径都被确定,或者优先队列为空时,算法结束。
Java 代码实现
下面是 Dijkstra 算法的 Java 实现:
import java.util.*;
public class DijkstraAlgorithm {
// 节点类
static class Node implements Comparable<Node> {
int vertex; // 节点编号
int weight; // 从起点到该节点的距离
Node(int vertex, int weight) {
this.vertex = vertex;
this.weight = weight;
}
@Override
public int compareTo(Node other) {
return Integer.compare(this.weight, other.weight);
}
}
// Dijkstra 算法实现
public static int[] dijkstra(int[][] graph, int start) {
int numVertices = graph.length; // 图的顶点数量
int[] dist = new int[numVertices]; // 距离数组
boolean[] visited = new boolean[numVertices]; // 访问标记数组
PriorityQueue<Node> minHeap = new PriorityQueue<>(); // 优先队列
// 初始化距离数组
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0; // 起点到自身的距离为 0
minHeap.add(new Node(start, 0)); // 将起点加入优先队列
while (!minHeap.isEmpty()) {
Node currentNode = minHeap.poll(); // 取出距离最小的节点
int currentVertex = currentNode.vertex;
// 如果当前节点已经访问过,则跳过
if (visited[currentVertex]) {
continue;
}
visited[currentVertex] = true; // 标记为已访问
// 遍历邻接节点
for (int neighbor = 0; neighbor < numVertices; neighbor++) {
if (graph[currentVertex][neighbor] != 0 && !visited[neighbor]) {
int newDist = dist[currentVertex] + graph[currentVertex][neighbor];
// 如果新路径更短,则更新距离
if (newDist < dist[neighbor]) {
dist[neighbor] = newDist;
minHeap.add(new Node(neighbor, newDist)); // 将更新的节点加入优先队列
}
}
}
}
return dist; // 返回从起点到各个节点的最短距离
}
public static void main(String[] args) {
// 示例图的邻接矩阵表示
int[][] graph = {
{0, 7, 9, 0, 0, 14},
{7, 0, 10, 15, 0, 0},
{9, 10, 0, 11, 0, 2},
{0, 15, 11, 0, 6, 0},
{0, 0, 0, 6, 0, 9},
{14, 0, 2, 0, 9, 0}
};
int startVertex = 0; // 起点
int[] distances = dijkstra(graph, startVertex);
System.out.println("从起点到各个节点的最短距离:");
for (int i = 0; i < distances.length; i++) {
System.out.println("到节点 " + i + " 的距离: " + distances[i]);
}
}
}
代码解读
-
Node 类:
- 定义了一个
Node
类,用于表示图中的节点及其与起点的距离。 - 实现了
Comparable
接口,以便可以将节点添加到优先队列中,按距离进行排序。
- 定义了一个
-
dijkstra 方法:
- 输入:邻接矩阵
graph
和起点start
。 - 输出:返回从起点到各个节点的最短距离数组
dist
。 dist
数组初始化为 ∞,并将起点的距离设为 0。- 使用优先队列
minHeap
存储待处理的节点。
- 输入:邻接矩阵
-
主要逻辑:
- 从优先队列中提取当前距离最小的节点
currentNode
。 - 如果该节点已经被访问,直接跳过。
- 遍历与当前节点相邻的节点,更新距离,如果发现更短的路径,就更新距离并将该节点加入优先队列。
- 从优先队列中提取当前距离最小的节点
-
main 方法:
- 创建了一个图的邻接矩阵表示。
- 调用
dijkstra
方法并输出结果。
复杂度分析
- 时间复杂度:
- 使用优先队列时,Dijkstra 算法的时间复杂度为 O((V+E)logV)O((V + E) \log V)O((V+E)logV),其中 VVV 是顶点数,EEE 是边数。
- 空间复杂度:
- 主要取决于存储图的结构和距离数组,空间复杂度为 O(V)O(V)O(V)。
结论
Dijkstra 算法是一个高效且易于实现的算法,广泛应用于路径搜索问题。在实际应用中,合理选择数据结构可以显著提升算法的性能。