接上篇需求分析:
https://www.cnblogs.com/Shevewinyei/p/13849379.html
一、算法描述:
迪傑斯特拉算法(Dijkstra)是由荷蘭計算機科學家狄克斯特拉於1959 年提出的,因此又叫狄克斯特拉算法。是從一個頂點到其余各頂點的最短路徑算法,解決的是有權圖中最短路徑問題。迪傑斯特拉算法主要特點是從起始點開始,采用貪心算法的策略,每次遍歷到始點距離最近且未訪問過的頂點的鄰接節點,直到擴展到終點為止。
完整代碼的github地址:https://github.com/Shevewinyei/SubwayChange
二、核心代碼:
public static int Dijkstra(int startId,int endId,int[][] graph,int[] visit,int[] pre) {
//節點個數
int n = graph.length;
PriorityQueue<Node> pq = new PriorityQueue<>(new Node());
//將起點加入pq
pq.add(new Node(startId, 0));
while (!pq.isEmpty()){
Node t = pq.poll();
//當前節點是終點,即可返回最短路徑
if(t.node == endId)
return t.cost;
//t節點表示還未訪問
if (visit[t.node]==0){
//將節點設置為已訪問
visit[t.node] = -1;
//將當前節點相連且未訪問的節點遍歷
for (int i = 0; i < n; i++) {
if (graph[t.node][i]!=0 && visit[i]==0) {
pq.add(new Node(i, t.cost + graph[t.node][i]));
pre[i] = t.node;
}
}
}
}
return -1;
}
三、解題思路:
四、station站點類代碼:
public class station {
String stationName = ""; //站點名稱
ArrayList<String> LineID = new ArrayList<String>(); //站點所在的線路
ArrayList<station> AdjacentStations = new ArrayList<station>(); //相鄰站點
boolean IsTransfer = false; //站點是否是換乘站
//設置站點名稱
public void setName(String name) {
this.stationName = name;
}
//添加站點所在線路信息
public void addLineName(String id) {
this.LineID.add(id);
//如果站點所在線路出現多條,則可作為換乘點
if(LineID.size()>1) {
IsTransfer = true;
}
}
public void addAdjacentStations(station t) {
this.AdjacentStations.add(t);
}
public String getStationName() {
return this.stationName;
}
public ArrayList<String> getLineName() {
return this.LineID;
}
public ArrayList<station> getAdjacentStations(){
return this.AdjacentStations;
}
public boolean getIsTransfer() {
return this.IsTransfer;
}
}
如果覺得代碼比較繁瑣難讀懂,可以直接看下面的圖。
五、讀取txt文件中的數據,構造包含所用station的集合(不重復)。在讀取數據過程中,不斷更新鄰接矩陣。
/ /讀取地鐵線路數據,創建站點數組
public static void AddStation() throws FileNotFoundException {
Scanner in = new Scanner(new File("/Users/shenwenyan/eclipse-workspace/subwayChange/src/SubwayMessage.txt"));
while(in.hasNextLine()) {
String temp = in.nextLine();
String[] tokens = temp.split(" ");
//線路名稱
String lineName = tokens[0];
for(int i=1;i<tokens.length;i++) {
//先搜索list中是否存在該站點名稱,則只添加線路和相鄰節點(去重)
boolean t = false; //判斷是否存在arraylist中
for(int j=0;j<stations.size()-1;j++) {
if(stations.get(j).getStationName().equals(tokens[i])) {
stations.get(j).addLineName(lineName);
t = true;
break;
}
}
if(t==false) {
station a = new station();
a.setName(tokens[i]);
a.addLineName(lineName);
stations.add(a);
}
}
//添加相鄰站點
for(int i=1;i<tokens.length-1;i++) {
ADDAdjacentStations(tokens[i], tokens[i+1]);
}
}
}
//添加相鄰節點並更新鄰接矩陣
public static void ADDAdjacentStations(String name1,String name2) {
station t1 = findStation(name1);
station t2 = findStation(name2);
if(t1!=null && t2!=null) {
t1.addAdjacentStations(t2);
t2.addAdjacentStations(t1);
int x = findStationIndex(name1);
int y = findStationIndex(name2);
edges[x][y] = 1;
edges[y][x] = 1;
}
else {
//System.out.println("未找到該名稱的站點!!!");
}
}
六、鄰接矩陣更新完畢后,運用Dijkstra算法得出最短路徑,並把結果打印出來。打印路徑代碼如下:
//打印路徑並在其中判斷是否換乘
public static void PrintPath(int startId,int endId) {
Stack<Integer> Path = new Stack<Integer>();
int end = endId;
//前置節點入棧,使得輸出時候為正序
while(endId!=startId) {
Path.add(endId);
int temp = pre[endId];
endId = temp;
}
String lineString = "";
String nextLineString = "";
lineString = IsinOneLine(stations.get(startId), stations.get(Path.peek()));
System.out.println(stations.get(startId).getStationName()+lineString);
int i;
while(true){
i = Path.pop();
if(Path.isEmpty()) break;
nextLineString = IsinOneLine(stations.get(i), stations.get(Path.peek()));
//判斷是否換線
if(nextLineString.equals(lineString)) {
//不換線
System.out.print(" ");
System.out.print("------->");
System.out.println(stations.get(i).getStationName());
}
else {
//換線
lineString = nextLineString;
System.out.print(" ");
System.out.print("------->");
System.out.println(stations.get(i).getStationName());
System.out.println("在 "+stations.get(i).getStationName()+" 換乘 "+lineString);
}
}
System.out.print(" ");
System.out.print("------->");
System.out.println(stations.get(end).getStationName());
}
七、主函數代碼:
public static void main(String[] args) throws FileNotFoundException {
AddStation();
//特殊站點,手動加入連接
ADDAdjacentStations("T2航站樓", "三元橋");
ADDAdjacentStations("T3航站樓", "T2航站樓");
//輸入起點站和終到站
System.out.print("請輸入起點站:");
Scanner in = new Scanner(System.in);
String startNameString = in.next();
System.out.print("請輸入終點站:");
in = new Scanner(System.in);
String endNameString = in.next();
//找到起點站和終點站
station startStation = findStation(startNameString);
station endStation = findStation(endNameString);
if(startStation==null&&endStation!=null){
System.out.println("起點站不存在!!!");
}
else if(endStation==null&&startStation!=null){
System.out.println("終點站不存在!!!");
}
else if(endStation==null&&startStation==null) {
System.out.println("起點站和終點站都不存在!!!");
}
else {
System.out.println("正在搜索.....");
int startId = findStationIndex(startNameString);
int endId = findStationIndex(endNameString);
//找最短路徑
int[] visit = new int[Max]; //是否訪問
int dis = Dijkstra(startId, endId,edges,visit,pre);
System.out.println("經過總站點數:"+ dis);
if(dis!=-1 && dis!=0) {
//打印路徑
PrintPath(startId, endId);
}
else if(dis == -1){
System.out.println("無法到達!");
}
else if(dis == 0){
System.out.println("起點站和終點站為同一站!!!");
}
}
}
八、運行測試:
(1)測試輸入時候起點站輸入有誤的情況
(2)測試輸入時候終點站輸入有誤的情況
(3)測試輸入時候起點和終點都不存在的情況
(4)測試輸入同一個站點的情況
以上都是出現特殊情況的處理。以下測試正常查詢:
(1)測試同一線路上乘車情況
(2)測試換乘的情況
九、總結:
(1)這次的個人項目的實踐,對於我自己的編程能力有很大的幫助。學會如何去分析一個項目的需求,學會如何通過代碼實現。
(2)以上代碼還存在一定的不足之處,會通過后期的學習努力讓代碼更加完善完整。