package com.algorithm.greed;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @author zhoushixiong
* @Description Dijkstra 算法求单源最短路径
* 由于 Java 在执行的时候默认是不启动断言检查的(这个时候,所有的断言语句都将忽略!),要启用断言,请使用 JVM 参数: -enableassertions 或者 -ea
* @date 2020/11/18 8:58
**/
public class Dijkstra {
final static int MAX = Integer.MAX_VALUE;
/**
* 带权邻接矩阵
*/
private final int[][] matrix;
/**
* 根据输入的数据生成邻接矩阵以及初始化源点
*
* @param array 输入数组,格式:[(结点A,结点B,AB权重),...](结点从 0 开始)
* @param nodeCount 结点数
*/
public Dijkstra(int[][] array, int nodeCount) {
this.matrix = new int[nodeCount][nodeCount];
for (int i = 0; i < this.matrix.length; i++) {
for (int j = 0; j < this.matrix[i].length; j++) {
matrix[i][j] = MAX;
}
}
for (int[] node : array) {
assert node[0] >= 0 && node[0] < nodeCount && node[1] >= 0 && node[1] < nodeCount : "非法的结点";
assert node[2] > 0 : String.format("权重不在 (0 ~ %d] 区间范围内", MAX);
matrix[node[0]][node[1]] = node[2];
}
}
/**
* 在指定下标(indexs)范围内查找权重数组(dist)中最小值的下标
*
* @param dist 权重数组
* @param indexs 待查找的下标
* @return
*/
private int minNode(int[] dist, List<Integer> indexs) {
assert !indexs.isEmpty() : "下标范围集合不能为空";
int minValue = dist[indexs.get(0)], minIndex = indexs.get(0);
for (int i : indexs) {
if (dist[i] < minValue) {
minValue = dist[i];
minIndex = i;
}
}
return minIndex;
}
public Result figure(int origin) {
assert origin >= 0 && origin < matrix.length : String.format("源点不在 [%d ~ %d) 区间范围内", 0, matrix.length);
// step 1 初始化已选择结点集合 S 以及剩余结点集合 VS
List<Integer> s = Stream.of(origin).collect(Collectors.toList());
List<Integer> vs = IntStream.range(0, matrix.length).filter(item -> item != origin).boxed().collect(Collectors.toList());
// step 2 初始化各结点之间的最短距离数组 dist,以及结点的前驱结点数组 p
int[] dist = this.matrix[origin];
int[] p = Arrays.stream(dist).map(i -> i == MAX ? -1 : origin).toArray();
// step 3 遍历剩余结点
while (!vs.isEmpty()) {
int node = minNode(dist, vs);
s.add(node);
vs.remove(new Integer(node));
for (int i : vs) {
int sum;
// 如果结点和选定的结点有边相连,并且结点与选定结点的权重之和小于 dist 中结点的权重,则替换 dist 中的权重
if (this.matrix[node][i] != MAX && (sum = dist[node] + matrix[node][i]) < dist[i]) {
dist[i] = sum;
p[i] = node;
}
}
}
dist[origin] = 0;
return new Result(dist, p, origin);
}
public static class Result {
private int[] dist, p;
private int origin;
private Map<Integer, List> routes;
public Result(int[] dist, int[] p, int origin) {
this.dist = dist;
this.p = p;
this.origin = origin;
route();
}
/**
* 根据结点前驱数组 p 生成源点到每一个结点的路径
*/
private void route() {
this.routes = new HashMap<>();
for (int i = 0; i < p.length; i++) {
int index = i;
List route = new ArrayList();
do {
route.add(index);
routes.put(i, route);
} while ((index = p[index]) != -1);
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
routes.forEach((node, route) -> {
// 逆转路径
Collections.reverse(route);
result.append(String.format("%d 点到 %d 点的最短距离为 %d,走过的路径为:%s", origin, node, dist[node], route))
.append("\n");
});
return result.toString();
}
}
public static void main(String[] args) {
// 数据输入,格式:[(结点A,结点B,AB权重),...](结点从 0 开始)
int[][] array = {{0, 4, 12}, {4, 0, 8}, {0, 1, 16}, {1, 0, 29}, {4, 1, 32}, {1, 3, 13}, {3, 1, 27},
{0, 2, 15},
{2, 0, 21}, {2, 3, 7}, {3, 2, 19}};
Dijkstra dijkstra = new Dijkstra(array, 5);
// 输入源点
Scanner scanner = new Scanner(System.in);
System.out.print("请输入源点(范围 [0,4]): ");
int origin = Integer.parseInt(scanner.next());
Result result = dijkstra.figure(origin);
System.out.println(result);
}
}
Dijkstra 算法求单源最短路径—— Java 实现
最新推荐文章于 2021-03-09 13:06:10 发布