Java實(shí)現(xiàn)Dijkstra輸出最短路徑的實(shí)例
Java實(shí)現(xiàn)Dijkstra輸出指定起點(diǎn)到終點(diǎn)的最短路徑
前言:
最近在公司參加了一個(gè)比賽,其中涉及的一個(gè)問(wèn)題,可以簡(jiǎn)化成如是描述:一個(gè)二維矩陣,每個(gè)點(diǎn)都有權(quán)重,需要找出從指定起點(diǎn)到終點(diǎn)的最短路徑。
馬上就想到了Dijkstra算法,所以又重新溫故了一遍,這里給出Java的實(shí)現(xiàn)。
而輸出最短路徑的時(shí)候,在網(wǎng)上也進(jìn)行了查閱,沒(méi)發(fā)現(xiàn)什么標(biāo)準(zhǔn)的方法,于是在下面的實(shí)現(xiàn)中,我給出了一種能夠想到的比較精簡(jiǎn)的方式:利用prev[]數(shù)組進(jìn)行遞歸輸出。
package graph.dijsktra;
import graph.model.Point;
import java.util.*;
/**
* Created by MHX on 2017/9/13.
*/
public class Dijkstra {
private int[][] map; // 地圖結(jié)構(gòu)保存
private int[][] edges; // 鄰接矩陣
private int[] prev; // 前驅(qū)節(jié)點(diǎn)標(biāo)號(hào)
private boolean[] s; // S集合中存放到起點(diǎn)已經(jīng)算出最短路徑的點(diǎn)
private int[] dist; // dist[i]表示起點(diǎn)到第i個(gè)節(jié)點(diǎn)的最短路徑
private int pointNum; // 點(diǎn)的個(gè)數(shù)
private Map<Integer, Point> indexPointMap; // 標(biāo)號(hào)和點(diǎn)的對(duì)應(yīng)關(guān)系
private Map<Point, Integer> pointIndexMap; // 點(diǎn)和標(biāo)號(hào)的對(duì)應(yīng)關(guān)系
private int v0; // 起點(diǎn)標(biāo)號(hào)
private Point startPoint; // 起點(diǎn)
private Point endPoint; // 終點(diǎn)
private Map<Point, Point> pointPointMap; // 保存點(diǎn)和權(quán)重的映射關(guān)系
private List<Point> allPoints; // 保存所有點(diǎn)
private int maxX; // x坐標(biāo)的最大值
private int maxY; // y坐標(biāo)的最大值
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();
}
/**
* 打印指定起點(diǎn)到終點(diǎn)的最短路徑
*/
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二維數(shù)組中的所有點(diǎn)轉(zhuǎn)換成自己的結(jié)構(gòu)
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;
}
}
}
// 根據(jù)map上的權(quán)重初始化edges,當(dāng)然這種算法是沒(méi)有單獨(dú)加起點(diǎn)的權(quán)重的
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點(diǎn)(起點(diǎn))到i點(diǎn)最短路徑是9999,即不可達(dá)
// 則i節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)不存在
prev[i] = -1;
} else {
// 初始化i節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)為起點(diǎn),因?yàn)檫@個(gè)時(shí)候有最短路徑的都是與起點(diǎn)直接相連的點(diǎn)
prev[i] = v0;
}
}
dist[v0] = 0;
s[v0] = true;
}
/**
* dijkstra核心算法
*/
private void dijkstra() {
for (int i = 1; i < pointNum; ++i) { // 此時(shí)有pointNum - 1個(gè)點(diǎn)在U集合中,需要循環(huán)pointNum - 1次
int minDist = 9999;
int u = v0;
for (int j = 1; j < pointNum; ++j) { // 在U集合中,找到到起點(diǎn)最短距離的點(diǎn)
if (!s[j] && dist[j] < minDist) { // 不在S集合,就是在U集合
u = j;
minDist = dist[j];
}
}
s[u] = true; // 將這個(gè)點(diǎn)放入S集合
for (int j = 1; j < pointNum; ++j) { // 以當(dāng)前剛從U集合放入S集合的點(diǎn)u為基礎(chǔ),循環(huán)其可以到達(dá)的點(diǎ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)); // 剔除不在地圖范圍內(nèi)的null點(diǎn)
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}
}; // 每個(gè)點(diǎn)都代表權(quán)重,沒(méi)有方向限制
Point startPoint = new Point(0, 3); // 起點(diǎn)
Point endPoint = new Point(5, 6); // 終點(diǎn)
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;
}
}
如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望通過(guò)本文能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
java應(yīng)用程序如何自定義log4j配置文件的位置
這篇文章主要介紹了java應(yīng)用程序如何自定義log4j配置文件的位置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
java對(duì)xml節(jié)點(diǎn)屬性的增刪改查實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇java對(duì)xml節(jié)點(diǎn)屬性的增刪改查實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
Java并發(fā)編程之CountDownLatch原理詳解
這篇文章主要介紹了Java并發(fā)編程之CountDownLatch原理詳解,CountDownLatch類(lèi)中使用了一個(gè)繼承自AQS的共享鎖Sync對(duì)象,構(gòu)造CountDownLatch對(duì)象時(shí)會(huì)將傳入的線(xiàn)程數(shù)值設(shè)為AQS的state值,需要的朋友可以參考下2023-12-12
搭建MyBatis-Plus框架并進(jìn)行數(shù)據(jù)庫(kù)增刪改查功能
這篇文章主要介紹了搭建MyBatis-Plus框架并進(jìn)行數(shù)據(jù)庫(kù)增刪改查,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Spring-AOP @AspectJ進(jìn)階之如何綁定代理對(duì)象
這篇文章主要介紹了Spring-AOP @AspectJ進(jìn)階之如何綁定代理對(duì)象的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Intellij IDEA集成JProfiler性能分析工具
作為Java程序員,性能分析是我們必須掌握的技能之一,在性能分析中,JProfiler是一款非常強(qiáng)大的工具,本文就來(lái)介紹一下Intellij IDEA集成JProfiler性能分析工具,就有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Spring Boot集成LangChain來(lái)實(shí)現(xiàn)Rag應(yīng)用的問(wèn)題小結(jié)
檢索增強(qiáng)生成(RAG)是一種優(yōu)化大型語(yǔ)言模型(LLM)輸出的技術(shù),通過(guò)引用權(quán)威知識(shí)庫(kù)以增強(qiáng)模型的準(zhǔn)確性和相關(guān)性,RAG允許LLM在不重新訓(xùn)練的情況下訪(fǎng)問(wèn)特定領(lǐng)域的知識(shí),提高了其在各種應(yīng)用中的實(shí)用性和信任度,感興趣的朋友跟隨小編一起看看吧2024-09-09

