北京地铁最短路径(Java+Dijkstra算法)

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

posted @ 2020-11-04 18:03  Nikole  阅读(1393)  评论(0编辑  收藏  举报