接上篇需求分析:
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)以上代码还存在一定的不足之处,会通过后期的学习努力让代码更加完善完整。