Java带权图的最短路径算法-Dijkstra
Dijkstra算法的核心思想是通过逐步逼近的方式,找出从起点到图中其他所有节点的最短路径。算法的基本步骤如下:
-
初始化:
- 创建一个最短路径信息数组shortPath[x][3],每一个一维数组含义为当前结点、该节点到此节点的最短路径、起始节点。
- 初始化shortPath数组,shortPath[x][0]当前节点编号、shortPath[x][1]最短路径、shortPath[x][2]起始结点编号
- 初始化优先队列,将起始节点的所有邻接点加入到优先队列中,结点信息使用Ownership类,属性值{time、nodeIndex、weight}。
- 创建优先队列,优先队列按照结点的权重值优先出队 PriorityQueue。
- 创建优先队列的比较器OwnershipCustomerComparator类,通过weight大小进行优先出队。
-
流程:
- 优先队列为空则退出
- 遍历优先队列,将队列中time版本对应的结点信息值写入shortPath中。每次拿到最新路径长度newWeight=matrix[up - 1][ownership.nodeIndex] + shortPath[up - 1][1](起始节点最短路径+起始节点到当前节点一条边的权重),如果当前结点未被初始化则直接将newWeight写入shortPath数组中,如果当前节点已经被写过最短路径,则直接略过当前newWeight即可,这里有一个dtx变量,记录当前优先队列中结点是否被写如果shortPath,用于time(版本)。
- 遍历优先队列(需要出队),将权重最小的结点出队,将该节点下的所有邻接点拿出做以下操作步骤:
- 需要是出对节点的邻接点
- 邻接点在shortPath表中的最短路径未被初始化(还是无穷大),将结点信息写入,最短路径为出对节点权重+出对节点到达该邻接点的权重
- 查看该邻接点是否出现在优先队列中。在优先队列中则更新shortPath数组以及优先队列中该结点的权重以及起始节点的信息。
- 优先队列中没有,Math.min(newWeight, shortPath[i][1]),取最优路径写入
代码
public static void ownership_graph() { DirectGraph graph = new DirectGraph(new String[]{"V1", "V2", "V3", "V4", "V5"}, true); graph.addEdge(1, 2, 10); graph.addEdge(1, 4, 30); graph.addEdge(1, 5, 90); graph.addEdge(2, 3, 50); graph.addEdge(3, 5, 10); graph.addEdge(4, 2, 10); graph.addEdge(4, 3, 20); graph.addEdge(4, 5, 60); int[][] shortPath = graph.dijkstraShortFinding(1); for (int i = 0; i < shortPath.length; i++) { System.out.printf("V1 -> V%d 的最短路径为: %d\n", i + 1, shortPath[i][1]); } } /** * 有向图 * @author sean * @version 1.0 * @date 2024-3-19 15:34 */ public class DirectGraph { /** * 结点集合 */ private List<String> node; /** * true 有权图 * false 无权图 */ private boolean weightFlag; /** * 邻接矩阵 */ private int[][] matrix; public DirectGraph(String[] nodes, boolean weightFlag) { node = new ArrayList<>(Arrays.asList(nodes)); matrix = new int[node.size()][node.size()]; this.weightFlag = weightFlag; } /** * 有权图的最短路径 * @param x 起始结点 * @return 有权图最短路径 说明数组 */ public int[][] dijkstraShortFinding(int x) { if (this.weightFlag) { // 优先队列 PriorityQueue<Ownership> queue = new PriorityQueue<>(new OwnershipCustomerComparator()); for (int i = 0; i < matrix[x - 1].length; i++) { if (matrix[x - 1][i] != 0) { queue.offer(new Ownership(1, i, matrix[x - 1][i])); } } // queue -> v1 v6 int[][] shortPath = new int[node.size()][3]; initOwnershipShortPath(shortPath); // 表示被访问过 表示到2的距离 表示目的节点编号从1开始 shortPath[x - 1][1] = 0; shortPath[x - 1][2] = x; ownershipShortPath(shortPath, queue, x, 0); return shortPath; } return new int[node.size()][3]; } /** * 初始化有权数组 * @param shortPath 最短路径数组 */ private void initOwnershipShortPath(int[][] shortPath) { for (int i = 0; i < shortPath.length; i++) { shortPath[i][0] = i + 1; shortPath[i][1] = -1; shortPath[i][2] = 0; } } /** * 有权图的最短路径 * @param shortPath 最短路径数组 * @param queue 按照权值的优先队列 * @param up 起始节点 * @param time 版本号 是否需要修改当前结点在 shortPath中的属性 */ private void ownershipShortPath(int[][] shortPath, PriorityQueue<Ownership> queue, int up, int time) { if (queue.isEmpty()) { return; } int dtx = 0; for (Ownership ownership : queue) { if (ownership.time != time + 1) { continue; } // 新的路径长度 int newWeight = matrix[up - 1][ownership.nodeIndex] + shortPath[up - 1][1]; // 总是拿到最优路径 if (shortPath[ownership.nodeIndex][1] == -1) { shortPath[ownership.nodeIndex][1] = newWeight; shortPath[ownership.nodeIndex][2] = up; } else { if (shortPath[ownership.nodeIndex][1] >= newWeight) { shortPath[ownership.nodeIndex][1] = newWeight; shortPath[ownership.nodeIndex][2] = up; } } dtx = 1; } // 遍历优先队列 while (!queue.isEmpty()) { // 删除的结点 Ownership removeNode = queue.poll(); // 该节点是否影响其他结点 boolean flag = false; for (int i = 0; i < matrix[removeNode.nodeIndex].length; i++) { int newWeight = removeNode.weight + matrix[removeNode.nodeIndex][i]; // 当前节点的邻接点 必要条件 if (matrix[removeNode.nodeIndex][i] != 0) { // 当前邻接点的最小距离 为 无穷大 就直接将当前路径长度放入 if (shortPath[i][1] == -1) { queue.offer(new Ownership(removeNode.time + dtx, i, newWeight)); flag = true; continue; } if (newWeight > shortPath[i][1]) { continue; } // 优先队列中包含了当前结点 并且 路径为最优 if (queue.contains(new Ownership(i))) { // 更新队列中结点的权值 和 最短路径数组中的属性 for (Ownership ownership : queue) { if (ownership.nodeIndex == i) { ownership.weight = newWeight; shortPath[i][2] = removeNode.nodeIndex + 1; shortPath[i][1] = ownership.weight; break; } } } else { queue.offer(new Ownership(removeNode.time + dtx, i, newWeight)); flag = true; } } } // 可影响其他结点 在进行 递归回溯 if (flag) { ownershipShortPath(shortPath, queue, removeNode.nodeIndex + 1, time + 1); } } } /** * 有点队列比较器 */ static class OwnershipCustomerComparator implements Comparator<Ownership>{ @Override public int compare(Ownership o1, Ownership o2) { return o1.weight - o2.weight; } } /** * 结点信息 */ static class Ownership{ int time; int nodeIndex; int weight; public Ownership(int nodeIndex, int weight) { this.nodeIndex = nodeIndex; this.weight = weight; } public Ownership(int time, int nodeIndex, int weight) { this.time = time; this.nodeIndex = nodeIndex; this.weight = weight; } public Ownership(int nodeIndex) { this.nodeIndex = nodeIndex; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Ownership ownership = (Ownership) o; return nodeIndex == ownership.nodeIndex; } } }
## 结果 V1 -> V1 的最短路径为: 0 V1 -> V2 的最短路径为: 10 V1 -> V3 的最短路径为: 50 V1 -> V4 的最短路径为: 30 V1 -> V5 的最短路径为: 60
初学图论算法,在B站看到有博主讲的思路,就模拟下来啦,感觉代码写的很多冗余,希望大佬指点,感谢🙇🙇