北京地鐵最短路徑(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)以上代碼還存在一定的不足之處,會通過后期的學習努力讓代碼更加完善完整。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM