最近学习《算法图解》一书时,看到狄克斯特拉算法之前没有使用过,在学习后用常用语言Java将算法实现出来以加深印象。
作用
狄克斯特拉算法用于找出最快的路径,而常用的广度优先搜索算法可用于找出最短的路径。
限制
狄克斯特拉算法作用于:单向、加权图且无负权边
算法步骤
- 在未处理的节点中找出开销最小的节点
- 遍历开销最小节点的邻居(邻边)
- 如果经当前节点前往该邻居更近,则更新到达该邻居的开销
- 同时将该邻居的父节点设置为当前节点
- 遍历完当前节点的邻边后,将当前节点标记为已处理
- 找出下一个开销最小且未处理过的节点,循环上面的步骤
目标图
算法步骤
1. 将图转换成数据结构
这里将图转换成Map
/**
* 将图转化成Map
*
* @return
*/
private static Map<String, Map<String, Integer>> initGraphMap() {
Map<String, Integer> startPoint = new HashMap<>();
startPoint.put("A", 6);
startPoint.put("B", 2);
Map<String, Integer> aPoint = new HashMap<>();
aPoint.put("final", 1);
Map<String, Integer> bPoint = new HashMap<>();
bPoint.put("A", 3);
bPoint.put("final", 5);
Map<String, Map<String, Integer>> graphMap = new HashMap<>();
graphMap.put("start", startPoint);
graphMap.put("A", aPoint);
graphMap.put("B", bPoint);
graphMap.put("final", new HashMap<>());
return graphMap;
}
2. 初始化记录达到每个节点的花销数据结构
因为我们肯定是从起点开始,所以需要先将起点能够达到的所有邻边的花销记录起来
private static void initMap(String startPoint) {
// 通过起点找关联的邻边
Map<String, Integer> pointMap = GRAPH_MAP.get(startPoint);
// 如果起点没有邻边,就直接返回结束
if (pointMap == null) {
return;
}
// 一开始起点能到达的地方,且需要花费的金钱,就等于costsMap的初始化Map
COSTS_MAP = pointMap;
// 需要给costsMap加上达到终点的花费,默认为Integer.MAX_VALUE
COSTS_MAP.put("final", Integer.MAX_VALUE);
// 初始化parentsMap,维护好起点和邻边的关系
for (Map.Entry<String, Integer> pointEntry : pointMap.entrySet()) {
// 起点能达到的地方
String point = pointEntry.getKey();
PARENTS_MAP.put(point, "start");
}
}
3. 找出开销最小且未处理过的节点
private static String findLowestCostPoint() {
Integer minCost = Integer.MAX_VALUE;
String targetPoint = null;
// 遍历costsMap,找出花费最小的节点
for (Map.Entry<String, Integer> costEntry : COSTS_MAP.entrySet()) {
String point = costEntry.getKey();
Integer cost = costEntry.getValue();
if (cost < minCost && !HISTORY_SET.contains(point)) {
minCost = cost;
targetPoint = point;
}
}
return targetPoint;
}
4. 遍历开销最小节点的邻边,如果经当前节点前往邻边更近,则更新到达该邻边的开销,并且维护父节点的关系
public static void main(String[] args) {
if (COSTS_MAP == null) {
System.out.println("地图为空!");
return;
}
String lowestCostPoint = findLowestCostPoint();
while (lowestCostPoint != null) {
// 首先拿到到达给当前最小花费节点所需费用
Integer cost = COSTS_MAP.get(lowestCostPoint);
// 获取到最小花费节点所能到达的节点列表
Map<String, Integer> targetPointMap = GRAPH_MAP.get(lowestCostPoint);
// 遍历最小花费节点所能到达的节点,并计算所需花费
for (Map.Entry<String, Integer> targetEntry : targetPointMap.entrySet()) {
String currentPoint = targetEntry.getKey();
// 到达当前节点所需费用
Integer newCost = targetEntry.getValue() + cost;
// 判断当前所能到达的节点,是否已经存在costsMap当中
if (COSTS_MAP.containsKey(currentPoint)) {
// 如果存在,则判断不同路径到达该节点的费用哪个更便宜
if (newCost < COSTS_MAP.get(currentPoint)) {
// 如果新的路径更便宜,则更新costsMap以及parentsMap
COSTS_MAP.put(currentPoint, newCost);
PARENTS_MAP.put(currentPoint, lowestCostPoint);
}
continue;
}
// 如果不存在,则直接更新costsMap以及parentsMap
COSTS_MAP.put(currentPoint, newCost);
PARENTS_MAP.put(currentPoint, lowestCostPoint);
}
// 将当前节点从添加到已处理列表中
HISTORY_SET.add(lowestCostPoint);
// 再次寻找花费最小的节点
lowestCostPoint = findLowestCostPoint();
}
}
开销图的执行经过,绿色背景表示已经处理过,下次不可再处理的节点:
父节点关系维护图的执行经过,通过这个关系图,我们可以还原整个最快路径:
Show me the code
public class Dijkstra {
// GRAPH,将图转换成Map
private static Map<String, Map<String, Integer>> GRAPH_MAP;
// COSTS,记录到达每个节点的花费
private static Map<String, Integer> COSTS_MAP;
// PARENTS,记录父节点路径
private static Map<String, String> PARENTS_MAP = new HashMap<>();
// 记录已经处理过的节点
private static Set<String> HISTORY_SET = new HashSet<>();
public static void main(String[] args) {
// 将图初始化成Map
GRAPH_MAP = initGraphMap();
// 根据graphMap,计算出初始化的花费和父节点Map
initMap("start");
if (COSTS_MAP == null) {
System.out.println("地图为空!");
return;
}
String lowestCostPoint = findLowestCostPoint();
while (lowestCostPoint != null) {
// 首先拿到到达给当前最小花费节点所需费用
Integer cost = COSTS_MAP.get(lowestCostPoint);
// 获取到最小花费节点所能到达的节点列表
Map<String, Integer> targetPointMap = GRAPH_MAP.get(lowestCostPoint);
// 遍历最小花费节点所能到达的节点,并计算所需花费
for (Map.Entry<String, Integer> targetEntry : targetPointMap.entrySet()) {
String currentPoint = targetEntry.getKey();
// 到达当前节点所需费用
Integer newCost = targetEntry.getValue() + cost;
// 判断当前所能到达的节点,是否已经存在costsMap当中
if (COSTS_MAP.containsKey(currentPoint)) {
// 如果存在,则判断不同路径到达该节点的费用哪个更便宜
if (newCost < COSTS_MAP.get(currentPoint)) {
// 如果新的路径更便宜,则更新costsMap以及parentsMap
COSTS_MAP.put(currentPoint, newCost);
PARENTS_MAP.put(currentPoint, lowestCostPoint);
}
continue;
}
// 如果不存在,则直接更新costsMap以及parentsMap
COSTS_MAP.put(currentPoint, newCost);
PARENTS_MAP.put(currentPoint, lowestCostPoint);
}
// 将当前节点从添加到已处理列表中
HISTORY_SET.add(lowestCostPoint);
// 再次寻找花费最小的节点
lowestCostPoint = findLowestCostPoint();
}
// 得到最短路径
List<String> path = getPath();
System.out.println("最短路径为:" + path.toString());
}
private static List<String> getPath() {
List<String> path = new LinkedList<>();
String parent = PARENTS_MAP.get("final");
path.add(0, "final");
while (parent != null) {
path.add(0, parent);
parent = PARENTS_MAP.get(parent);
}
return path;
}
private static String findLowestCostPoint() {
Integer minCost = Integer.MAX_VALUE;
String targetPoint = null;
// 遍历costsMap,找出花费最小的节点
for (Map.Entry<String, Integer> costEntry : COSTS_MAP.entrySet()) {
String point = costEntry.getKey();
Integer cost = costEntry.getValue();
if (cost < minCost && !HISTORY_SET.contains(point)) {
minCost = cost;
targetPoint = point;
}
}
return targetPoint;
}
private static void initMap(String startPoint) {
Map<String, Integer> pointMap = GRAPH_MAP.get(startPoint);
if (pointMap == null) {
return;
}
// 一开始起点能到达的地方,且需要花费的金钱,就等于costsMap的初始化Map
COSTS_MAP = pointMap;
// 需要给costsMap加上达到终点的花费,默认为Integer.MAX_VALUE
COSTS_MAP.put("final", Integer.MAX_VALUE);
// 初始化parentsMap
for (Map.Entry<String, Integer> pointEntry : pointMap.entrySet()) {
// 起点能达到的地方
String point = pointEntry.getKey();
PARENTS_MAP.put(point, "start");
}
}
/**
* 将图转化成Map
*
* @return
*/
private static Map<String, Map<String, Integer>> initGraphMap() {
Map<String, Integer> startPoint = new HashMap<>();
startPoint.put("A", 6);
startPoint.put("B", 2);
Map<String, Integer> aPoint = new HashMap<>();
aPoint.put("final", 1);
Map<String, Integer> bPoint = new HashMap<>();
bPoint.put("A", 3);
bPoint.put("final", 5);
Map<String, Map<String, Integer>> graphMap = new HashMap<>();
graphMap.put("start", startPoint);
graphMap.put("A", aPoint);
graphMap.put("B", bPoint);
graphMap.put("final", new HashMap<>());
return graphMap;
}
}
结尾
算法的实现以及圈复杂度都存在优化的空间,后续想优化的时候再优化~
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/77867.html