Java實現Dijkstra輸出最短路徑的實例
Java實現Dijkstra輸出指定起點到終點的最短路徑
前言:
最近在公司參加了一個比賽,其中涉及的一個問題,可以簡化成如是描述:一個二維矩陣,每個點都有權重,需要找出從指定起點到終點的最短路徑。
馬上就想到了Dijkstra算法,所以又重新溫故了一遍,這里給出Java的實現。
而輸出最短路徑的時候,在網上也進行了查閱,沒發(fā)現什么標準的方法,于是在下面的實現中,我給出了一種能夠想到的比較精簡的方式:利用prev[]數組進行遞歸輸出。
package graph.dijsktra;
import graph.model.Point;
import java.util.*;
/**
* Created by MHX on 2017/9/13.
*/
public class Dijkstra {
private int[][] map; // 地圖結構保存
private int[][] edges; // 鄰接矩陣
private int[] prev; // 前驅節(jié)點標號
private boolean[] s; // S集合中存放到起點已經算出最短路徑的點
private int[] dist; // dist[i]表示起點到第i個節(jié)點的最短路徑
private int pointNum; // 點的個數
private Map<Integer, Point> indexPointMap; // 標號和點的對應關系
private Map<Point, Integer> pointIndexMap; // 點和標號的對應關系
private int v0; // 起點標號
private Point startPoint; // 起點
private Point endPoint; // 終點
private Map<Point, Point> pointPointMap; // 保存點和權重的映射關系
private List<Point> allPoints; // 保存所有點
private int maxX; // x坐標的最大值
private int maxY; // y坐標的最大值
public Dijkstra(int map[][], Point startPoint, Point endPoint) {
this.maxX = map.length;
this.maxY = map[0].length;
this.pointNum = maxX * maxY;
this.map = map;
this.startPoint = startPoint;
this.endPoint = endPoint;
init();
dijkstra();
}
/**
* 打印指定起點到終點的最短路徑
*/
public void printShortestPath() {
printDijkstra(pointIndexMap.get(endPoint));
}
/**
* 初始化dijkstra
*/
private void init() {
// 初始化所有變量
edges = new int[pointNum][pointNum];
prev = new int[pointNum];
s = new boolean[pointNum];
dist = new int[pointNum];
indexPointMap = new HashMap<>();
pointIndexMap = new HashMap<>();
pointPointMap = new HashMap<>();
allPoints = new ArrayList<>();
// 將map二維數組中的所有點轉換成自己的結構
int count = 0;
for (int x = 0; x < maxX; ++x) {
for (int y = 0; y < maxY; ++y) {
indexPointMap.put(count, new Point(x, y));
pointIndexMap.put(new Point(x, y), count);
count++;
allPoints.add(new Point(x, y));
pointPointMap.put(new Point(x, y), new Point(x, y, map[x][y]));
}
}
// 初始化鄰接矩陣
for (int i = 0; i < pointNum; ++i) {
for (int j = 0; j < pointNum; ++j) {
if (i == j) {
edges[i][j] = 0;
} else {
edges[i][j] = 9999;
}
}
}
// 根據map上的權重初始化edges,當然這種算法是沒有單獨加起點的權重的
for (Point point : allPoints) {
for (Point aroundPoint : getAroundPoints(point)) {
edges[pointIndexMap.get(point)][pointIndexMap.get(aroundPoint)] = aroundPoint.getValue();
}
}
v0 = pointIndexMap.get(startPoint);
for (int i = 0; i < pointNum; ++i) {
dist[i] = edges[v0][i];
if (dist[i] == 9999) {
// 如果從0點(起點)到i點最短路徑是9999,即不可達
// 則i節(jié)點的前驅節(jié)點不存在
prev[i] = -1;
} else {
// 初始化i節(jié)點的前驅節(jié)點為起點,因為這個時候有最短路徑的都是與起點直接相連的點
prev[i] = v0;
}
}
dist[v0] = 0;
s[v0] = true;
}
/**
* dijkstra核心算法
*/
private void dijkstra() {
for (int i = 1; i < pointNum; ++i) { // 此時有pointNum - 1個點在U集合中,需要循環(huán)pointNum - 1次
int minDist = 9999;
int u = v0;
for (int j = 1; j < pointNum; ++j) { // 在U集合中,找到到起點最短距離的點
if (!s[j] && dist[j] < minDist) { // 不在S集合,就是在U集合
u = j;
minDist = dist[j];
}
}
s[u] = true; // 將這個點放入S集合
for (int j = 1; j < pointNum; ++j) { // 以當前剛從U集合放入S集合的點u為基礎,循環(huán)其可以到達的點
if (!s[j] && edges[u][j] < 9999) {
if (dist[u] + edges[u][j] < dist[j]) {
dist[j] = dist[u] + edges[u][j];
prev[j] = u;
}
}
}
}
}
private void printDijkstra(int endPointIndex) {
if (endPointIndex == v0) {
System.out.print(indexPointMap.get(v0) + ",");
return;
}
printDijkstra(prev[endPointIndex]);
System.out.print(indexPointMap.get(endPointIndex) + ",");
}
private List<Point> getAroundPoints(Point point) {
List<Point> aroundPoints = new ArrayList<>();
int x = point.getX();
int y = point.getY();
aroundPoints.add(pointPointMap.get(new Point(x - 1, y)));
aroundPoints.add(pointPointMap.get(new Point(x, y + 1)));
aroundPoints.add(pointPointMap.get(new Point(x + 1, y)));
aroundPoints.add(pointPointMap.get(new Point(x, y - 1)));
aroundPoints.removeAll(Collections.singleton(null)); // 剔除不在地圖范圍內的null點
return aroundPoints;
}
public static void main(String[] args) {
int map[][] = {
{1, 2, 2, 2, 2, 2, 2},
{1, 0, 2, 2, 0, 2, 2},
{1, 2, 0, 2, 0, 2, 2},
{1, 2, 2, 0, 2, 0, 2},
{1, 2, 2, 2, 2, 2, 2},
{1, 1, 1, 1, 1, 1, 1}
}; // 每個點都代表權重,沒有方向限制
Point startPoint = new Point(0, 3); // 起點
Point endPoint = new Point(5, 6); // 終點
Dijkstra dijkstra = new Dijkstra(map, startPoint, endPoint);
dijkstra.printShortestPath();
}
}
package graph.model;
public class Point {
private int x;
private int y;
private int value;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(int x, int y, int value) {
this.x = x;
this.y = y;
this.value = value;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Override
public String toString() {
return "{" +
"x=" + x +
", y=" + y +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
if (x != point.x) return false;
return y == point.y;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望通過本文能幫助到大家,謝謝大家對本站的支持!
相關文章
Java8?CompletableFuture?異步多線程的實現
本文主要介紹了Java8?CompletableFuture?異步多線程的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04
SpringCloudConfig之client端報錯Could?not?resolve?placeholder問
這篇文章主要介紹了SpringCloudConfig之client端報錯Could?not?resolve?placeholder?‘from‘?in?value?“${from}“問題及解決方案,具有很好的參考價值,希望對大家有所幫助2022-12-12
實例化JFileChooser對象報空指針異常問題的解決辦法
今天小編就為大家分享一篇關于實例化JFileChooser對象報空指針異常問題的解決辦法,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02
Spring事務控制策略及@Transactional失效問題解決避坑
這篇文章主要為大家介紹了Spring事務控制策略及@Transactional失效問題解決避坑,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06

