1. 程式人生 > 實用技巧 >地鐵線路最短路徑(程式碼實現)

地鐵線路最短路徑(程式碼實現)


提供一副地鐵線路圖,計算指定兩站之間最短(最少經過站數)乘車路線;
地鐵線路的資訊儲存在一份文字文件中,格式如下:

地鐵線路總數
線路名1 站名1 站名2 站名3 ...
線路名2 站名1 站名2 站名3 ...
線路名3 站名1 站名2 站名3 ......

主要功能
1:能夠讀取文字內容,並在使用者查詢時能夠正確顯示;
2:輸入起點和終點要求能夠輸出最短路徑站點總數;
3:輸出詳細的站點路線;

實現語言
Java

實現演算法
bfs演算法

類職責劃分
1、line類 :用於儲存路線名和該路線上的所有站點

public class line {

    private String LineName;  //線名
    private List<String> stations= new ArrayList<String>();   //該線路中所有站
    //裡面的get set add省略未放上
}

2、station類 :用於儲存各個站點的具體資訊(名字、所在路線、與之相鄰的點等資訊)

    private String StationName;  //站點名
    private List<String> Line = new ArrayList<String>();  //所線上路(換乘站有多條)
    private List<station> LinkStations= new ArrayList<station>();  //與之相鄰的站點
    private boolean visited;//是否訪問過該站點
    private String preStation;//本站之前訪問的站點
    private int distance=0;//本站距離起點站的站數
    private boolean change;//是否換乘 預設為false 用於結果
      //裡面的get set add省略未放上

3、主函式中

    public static HashMap<String,station> map = new HashMap<>();//方便查詢站點資訊
    public static List<line> LineSet= new ArrayList<>();//方便查詢路線

核心程式碼

1、line類


private String LineName;  //線名
    private List<String> stations= new ArrayList<String>();   //該線路中所有站

    public List<String> getStations() {
        return stations;
    }

    public void setStations(List<String> stations) {
        this.stations = stations;
    }

    public String getLineName() {
        return LineName;
    }

    public void setLineName(String lineName) {
        LineName = lineName;
    }

    public void stationAdd(String name){
        stations.add(name);
    }

2、station類

    private String StationName;  //站點名
    private List<String> Line = new ArrayList<String>();  //所線上路(換乘站有多條)
    private List<station> LinkStations= new ArrayList<station>();  //與之相鄰的站點
    private boolean visited;//是否訪問過該站點
    private String preStation;//本站之前訪問的站點
    private int distance=0;//本站距離起點站的站數
    private boolean change;//是否換乘 預設為false 用於結果

    public boolean ischange() {
        return change;
    }

    public void setchange(boolean change) {
        this.change = change;
    }

    public int getDistance() {
        return distance;
    }

    public void setDistance(int distance) {
        this.distance = distance;
    }

    public void AddStationLine(String name){
        Line.add(name);
    }

    public void AddLinkStation(station sta){
        LinkStations.add(sta);
    }

    public String getStationName() {
        return StationName;
    }

    public void setStationName(String stationName) {
        StationName = stationName;
    }

    public List<String> getLine() {
        return Line;
    }

    public void setLine(List<String> line) {
        Line = line;
    }

    public List<station> getLinkStations() {
        return LinkStations;
    }

    public void setLinkStations(List<station> linkStations) {
        LinkStations = linkStations;
    }

    public boolean isVisited() {
        return visited;
    }

    public void setVisited(boolean visited) {
        this.visited = visited;
    }

    public String getPreStation() {
        return preStation;
    }

    public void setPreStation(String preStation) {
        this.preStation = preStation;
    }

3、主函式
主要分為三個方法
1)主要執行程式碼


public static void main(String[] ards){

        read("D:\\homework\\軟體工程\\地鐵線路資訊.txt"); // 呼叫讀取檔案函式
        Scanner scanner = new Scanner(System.in);
	System.out.print("1:查詢該線路站點   2:查詢某站到某站的最短距離  請輸入操作序號:");
	int chance = scanner.nextInt();
        switch(chance) {
		case 1:{
			System.out.print("輸入想查詢的線路:");
			String search = scanner.next();
			for(line line2 : LineSet) {
				if(line2.getLineName().equals(search)) {
					for(int i = 1; i < line2.getStations().size(); i++	) {
						System.out.print(line2.getStations().get(i)+ " " );
					}
				}
			}
			break;
		}
		case 2:{
			System.out.print("請輸入起點站:");
            String start = scanner.next();
            System.out.print("請輸入終點站:");
            String end = scanner.next();
            
            if (!map.containsKey(start)){//判斷起點站是否存在
                System.out.println("起點站不存在");
            	break;
            }
            if (!map.containsKey(end)){//判斷終點站是否存在
            	System.out.println("終點站不存在");
                break;
            }
            if (start.equals(end)){//判斷起點站和終點站是否相同
                System.out.print("起點站與終點站相同 本站為" + end + "不需要乘坐地鐵");
                break;
            }
            bfs(start,end);
            PrintPath(start,end);
            break;
		}
		default:{
			System.out.println("請重新選擇");
			break;
		}
	}
        return;
    }

2)read函式,用於讀取檔案地鐵資訊


public static void read(String name) {//讀入線路資料
        try {
            InputStreamReader reader = new InputStreamReader(new FileInputStream(name));
            BufferedReader br = new BufferedReader(reader);
            String read = null;

            while((read = br.readLine()) != null) {  //直到讀到空行位置
                line line = new line();//當前存的line

                String[] stations = read.split(" ");//讀入的line對應的所有station,並通過空格分割成字串陣列
                line.setLineName(stations[0]);//修改其線路名

                for (int j = 1; j < stations.length - 1; j++) {
                    station station1 = new station();//當前line中的station
                    station station2 = new station();
                    if (map.containsKey(stations[j])) {//如果map中已存在該站點
                        station1 = map.get(stations[j]);
                        map.remove(stations[j]);
                    } else { 							//否則創造一個站點,並初始化,用於開始階段
                        station1.setStationName(stations[j]);
                        station1.setVisited(false);
                    }

                    if (map.containsKey(stations[j + 1])) {//如果map中已存在該站點的後一站
                        station2 = map.get(stations[j + 1]);
                        map.remove(stations[j + 1]);
                    } else { 								//否則創造一個站點,並初始化,用於開始階段
                        station2.setStationName(stations[j + 1]);
                        station2.setVisited(false);
                    }
                    if (!station1.getLine().contains(line.getLineName()))//如果當前站未加入line中,則在line中當前站名
                        station1.AddStationLine(line.getLineName());
                    if (!station2.getLine().contains(line.getLineName()))//如果當前站未加入line中,則在line中當前站名
                        station2.AddStationLine(line.getLineName());
                    if (!station1.getLinkStations().contains(station2))
                        station1.AddLinkStation(station2);
                    if (!station2.getLinkStations().contains(station1))
                        station2.AddLinkStation(station1);

                    station1.setPreStation(station1.getStationName());
                    station2.setPreStation(station2.getStationName());
                    map.put(stations[j], station1);//把station1重新放回map
                    map.put(stations[j + 1], station2);//把station2重新放回map
                    if (!line.getStations().contains(station1.getStationName())) {
                        line.stationAdd(station1.getStationName());
                    }
                    if (!line.getStations().contains(station2.getStationName())) {
                        line.stationAdd(station2.getStationName());
                    }
                }
                LineSet.add(line);//把線路加入總的LineSet中,用於輸出
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

3)bfs的實現函式


public static void bfs(String start, String end){
        for (String a :map.keySet()){  //將每個節點進行初始化操作
            map.get(a).setVisited(false);
            map.get(a).setDistance(0);
        }
        station now = new station();
        Queue<String> queue = new LinkedList<>();//使用佇列,實現bfs演算法

        now = map.get(start);
        queue.add(start);
        while(!queue.isEmpty()){
            String nowName = queue.remove();
            map.get(nowName).setVisited(true);
            if (now.getStationName().equals(end)){
                break;
            }
            for (station station1 :map.get(nowName).getLinkStations()){
                if(!map.get(station1.getStationName()).isVisited()){//未訪問過的臨近站點
                    map.get(station1.getStationName()).setPreStation(nowName);//為preStation賦值
                    map.get(station1.getStationName()).setDistance(map.get(nowName).getDistance()+1);//臨近站的距離為本站距離加1
                    queue.offer(station1.getStationName());
                }
            }
        }
    }

4)輸出路線和判斷換乘函式


public static void PrintPath(String start,String end){
        List<String> path = new ArrayList<>();
        Stack<String> printline = new Stack<>();
        int num = 1;//第幾站
        int cnt = 0;//換乘次數
        String str = end;
        while(!str.equals(start)){
            path.add(str); 
            printline.push(str);
            str = map.get(str).getPreStation();
        }
        path.add(str);//把start放入path
        printline.push(str);
        for (int i=1;i<path.size()-1;i++){
            if (map.get(path.get(i)).getLine().size() == 1){
                continue;
            }
            String temp1 = null;
            String temp2 = null;
            //每三戰判斷一次是否有換乘站
            for (String str1 : map.get(path.get(i)).getLine()){//本站與前一站的共同擁有的線路存在temp1中
                boolean flag = false;
                for (String str2 :map.get(path.get(i-1)).getLine()){
                    if (str1.equals(str2)){
                        temp1 = temp1 + str1;
                        flag = !flag;
                        break;
                    }
                }
                if (flag) break;
            }
            for (String str1 : map.get(path.get(i)).getLine()){//後一站與本站的共同擁有的線路存在temp2中
            	boolean flag = false;
                for (String str2 :map.get(path.get(i+1)).getLine()){
                    if (str1.equals(str2)){
                        temp2 = temp2 + str1;
                        flag = !flag;
                        break;
                    }
                }
                if (flag) break;
            }
            if (!temp1.equals(temp2))//若temp1和temp2兩線路不同則本站為轉乘站
                map.get(path.get(i)).setchange(true);                                                                                                                                
        }//判斷path中的換乘站
        System.out.println("共"+path.size()+"站");
        while(!printline.empty()){
            String printStation = printline.pop();
            if(num == 1){
                for (String strnow : map.get(printStation).getLine()){
                    for (String nextStation : map.get(path.get(path.size()-num-1)).getLine()){
                        if (strnow.equals(nextStation)) {
                            System.out.println("當前線為:"+strnow);
                            
                        }
                    }
                }
            }
            if (map.get(printStation).ischange()){
                String nowline ="";
                for (String strnow : map.get(printStation).getLine()){
                    //path.get(path.size()-numStation)為換乘站下一站 兩站共有的線就是換乘線
                    for (String nextStation : map.get(path.get(path.size()-num-1)).getLine()){
                        if (strnow.equals(nextStation))
                            nowline = nowline + strnow;
                    }
                }
                cnt++;
                System.out.println("");
                System.out.println("轉線為" + nowline);
            }
            System.out.print(printStation + " ");
            num++;
        }
    }

測試用例

查詢1號線線路時:

查詢最短路線時候

1、兩線存在一條線上時

2、兩線不存在一條線上時

查詢終點站不存在時

總結
此次實驗讓我發現我的程式設計能力非常的薄弱,日後有待提高;
這次是一一次寫部落格,有了一點點經驗,對日後一定有所幫助;
全部程式碼上交到https://github.com/chenyh1234/subway.git