北京地鐵線路推薦項目

#北京地鐵線路推薦項目 ##1、項目介紹 ###Github:github.com/LinXS597/BeijingSubway ##2、需求分析java

  1. 設計一個存儲線路信息的文件
  2. 實現地鐵線路信息的獲取
  3. 實現基礎查詢功能,查詢指定地鐵線路通過的站點
  4. 實現出發地到目的地最短路徑的查詢

##3、主要功能模塊介紹git

序號 模塊名稱 主要功能 對應Java文件
1 主模塊 流程控制、參數解析 src/cn/edu/zucc/Main.java
2 核心算法模塊 實現Dijkstra算法 src/cn/edu/zucc/core/DijkstraUtil.java
3 輸入輸出模塊 讀出和寫入txt文件數據 src/cn/edu/zucc/data/FileManager.java
4 存儲模塊 存儲讀入的站點與線路信息 src/cn/edu/zucc/model/Station.java
存儲輸出的推薦線路信息 src/cn/edu/zucc/model/Routine.java

###一、主模塊:流程控制、參數解析github

  • 支持採用參數 -map 做爲標誌,來獲取對應的自定義地鐵文件,例如:算法

    java subway -map subway.txt測試

  • 支持採用參數 -a 來指定地鐵線路,採用參數 -o 來輸出到指定文件station.txt,例如:命令行

    java subway -a 1號線 -map subway.txt -o station.txt設計

  • 支持採用參數 -b 來指定出發地與目的地,例如:code

    java subway -b 天安門西 北京大學東門 -map subway.txt -o routine.txtblog

###二、核心算法模塊:實現Dijkstra算法get

  • 利用 HashMap 存儲所有的站點信息,並以站點名稱爲key

    public static HashMap<String,Station> allStation = new HashMap<>();

  • 執行算法前先對數據進行預處理操做,主要目的有三個:

    1. 遍歷各條線路信息,將station去除重複後加入allStation之中,便於後續算法執行
    2. 根據傳入參數,肯定並設置routine的起點與終點
    3. 對一些特殊狀況進行處理,如起點終點重複、起點不存在、終點不存在等

具體代碼以下

public static Routine getRoutine ( String begin,String end)  {
        Routine routine = new Routine();
        List<Station> lineStationlist = null;


        Station station = null;
        Station repeatstation = null;

        for(String key:FileManager.subwayLineinfo.keySet()){    //遍歷各條線路信息,將station去除重後加入allStation之中

            lineStationlist = FileManager.subwayLineinfo.get(key);

            for(int i = 0;i<lineStationlist.size();i++){
                station = lineStationlist.get(i);
                if(allStation.keySet().contains(station.getStationName())){ //判斷是否重複
                    repeatstation = allStation.get(station.getStationName());

                    if (i==0){          //完善各個Sation中相鄰站點LinkStation信息
                        repeatstation.getLinkStations().add(lineStationlist.get(i+1));
                    }else if(i==lineStationlist.size()-1){
                        repeatstation.getLinkStations().add(lineStationlist.get(i-1));
                    }else{
                        repeatstation.getLinkStations().add(lineStationlist.get(i+1));
                        repeatstation.getLinkStations().add(lineStationlist.get(i-1));
                    }
                    continue;
                }
                else{

                    if (i==0){
                        station.getLinkStations().add(lineStationlist.get(i+1));
                    }else if(i==lineStationlist.size()-1){
                        station.getLinkStations().add(lineStationlist.get(i-1));
                    }else{
                        station.getLinkStations().add(lineStationlist.get(i+1));
                        station.getLinkStations().add(lineStationlist.get(i-1));
                    }

                    allStation.put(station.getStationName(),station);

                    if(station.getStationName().equals(begin)){     //根據傳入參數,肯定起點,並加入到routine之中
                        routine.setBeginStation(station);
                    }

                    if(station.getStationName().equals(end)){       //根據傳入參數,肯定終點,並加入到routine之中
                        routine.setEndStation(station);
                    }
                }
            }
        }

        if (routine.getBeginStation().equals(routine.getEndStation())){ //一些異常狀況的處理
            System.out.println("起點與終點相同,請從新輸入");
            return null;
        }
        else if (routine.getBeginStation() == null){
            System.out.println("起點不存在");
            return null;
        }
        else if (routine.getEndStation() == null){
            System.out.println("終點不存在");
            return null;
        }
        else{
            routine = new DijkstraUtil().Dijkstra_algorithm(routine);
        }

        return routine;
    }
  • 執行Dijkstra算法,主要有如下幾步:
    1. 定義起點到其餘頂點最短距離distance,定義某個指定站點的前一個站點path,並進行初始化 HashMap<HashMap<Station,Station>,Integer> distance = new HashMap<>(); HashMap<Station,Station> path = new HashMap<>();
    2. 選擇V,求的一個從起點到V的最短路徑
    3. 修改從起點到任一頂點W可達的最短路徑長度,以及W的父結點
    4. 重複二、3,求得最短距離

具體代碼以下:

public Routine Dijkstra_algorithm ( Routine routine ) {

        HashMap<HashMap<Station,Station>,Integer> distance = new HashMap<>();   //儲存各站點之間的最短距離,以Integer爲單位,表示站點數
        HashMap<Station,Integer> collected = new HashMap<>();                   //判斷該Station是否被訪問過
        HashMap<Station,Station> path = new HashMap<>();                        //存儲某個指定站點的前一個站點
        HashMap<Station,Station> disitem = new HashMap<>();
        Station item;
        Station V;

        for (String key :allStation.keySet()){      //初始化distance、collected與path
            item = allStation.get(key);

            collected.put(item,new Integer(0));

            if (!routine.getBeginStation().equals(item)){
                if (routine.getBeginStation().getLinkStations().contains(item)){    //若與起點相鄰,則將距離設置爲1,並將對應的path設置爲起點
                    disitem = new HashMap<>();
                    disitem.put(routine.getBeginStation(),item);
                    distance.put(disitem,new Integer(1));
                    path.put(item,routine.getBeginStation());
                }
                else{
                    disitem = new HashMap<>();                              //若未與起點相鄰,則將初值設置爲10000
                    disitem.put(routine.getBeginStation(),item);
                    distance.put(disitem,new Integer(10000));
                }
            }
            else{
                disitem = new HashMap<>();
                disitem.put(routine.getBeginStation(),item);
                distance.put(disitem,new Integer(0));
            }
        }

        collected.put(routine.getBeginStation(),1);

        while (true){
            V = FindMinDist(routine,distance,collected);    //取未被訪問頂點中distance最小者
            if (V.getStationName().equals("-1"))            //若這樣的V不存在,算法結束
                break;

            collected.put(V,1);

            for (String key:allStation.keySet()){           //遍歷每一個站點
                if (V.getLinkStations().contains(allStation.get(key))&&collected.get(allStation.get(key))==0){
                    if (distance.get(getFromtoFin(routine,V))+1<distance.get(getFromtoFin(routine,allStation.get(key)))){   //若收錄的頂點使distance變小,則進行更新
                        distance.put(getFromtoFin(routine,allStation.get(key)),distance.get(getFromtoFin(routine,V))+1);
                        path.put(allStation.get(key),V);
                    }
                }
            }
        }
        V = path.get(routine.getEndStation());

        while(!V.equals(routine.getBeginStation())){        //將最短路徑各站點數據存入routine之中
            routine.getPassStations().add(0,V);
            V = path.get(V);
        }
        routine.getPassStations().add(0,routine.getBeginStation());
        routine.getPassStations().add(routine.getEndStation());


        return routine;         //返回
    }

###三、輸入輸出模塊與存儲模塊:讀出和寫入txt文件數據

  • READ_FILE 和 WRITE_FILE 分別用於存儲讀入文件與輸出文件路徑信息
public  static String READ_FILE;
    public  static String WRITE_FILE;
  • 利用HashMap存儲線路數據

    public static HashMap<String, List<Station>> subwayLineinfo = new HashMap<>();

  • 存儲站點信息的Station類設計

private String stationName;     //站點名稱

    	private String lineName;        //線路名稱

    	private List<Station> linkStations = new ArrayList<>();     //相鄰站點
  • 存儲最短路徑信息的Rountine類設計
private Station beginStation;   //出發站點

    	private Station endStation;     //結束站點

    	private List<Station> passStations = new ArrayList<>();     //最短路徑上的站點
  • 讀入地鐵線路與站點數據

subway.txt 文件設計以下:

1號線 站點1 站點2 ...
	2號線 站點1 站點2 ...
	3號線 站點1 站點2 ...
	...
  • 寫入指定地鐵線路的站點信息

station.txt 文件設計以下:

1號線
	站點1
	站點2
	站點3
	...
  • 寫入指定站點之間的最短路徑上的站點信息

routine.txt 文件設計以下:

11
	天安門西
	西單
	復興門
	--->換乘地鐵--<2號線>--
	阜成門
	車公莊
	西直門
	--->換乘地鐵--<13號線>--
	大鐘寺
	知春路
	--->換乘地鐵--<10號線>--
	知春裏
	海淀黃莊
	--->換乘地鐵--<4號線大興線>--
	中關村
	北京大學東門

寫入時需判斷相鄰的兩個站點是否在同一條線路上,若是不在,則需更新線路信息並寫入routine.txt中

public static String getLineNmae(Station station1,Station station2){        //判斷兩個站點是否在同一條線路上
        String res = null;

        List<Station> item;
        
        for (String key : subwayLineinfo.keySet()){
            item = subwayLineinfo.get(key);
            if (item.contains(station1)&&item.contains(station2)){
                return key;     //若是是,返回線路名稱,不然,返回null
            }

        }

        return res;
    }

##4、測試分析

###1) 重要功能測試

  1. 讀入文件,命令行參數爲:-map subway.txt

    • 測試結果:成功
  2. 查詢線路,命令行參數爲:-a 1號線 -map subway.txt -o station.txt

    • 測試結果:成功

###2) 核心功能測試

  • 查詢最短線路,命令行參數爲:-b 天安門西 北京大學東門 -map subway.txt -o routine.txt

    • 測試結果:成功

###3) 異常狀況處理測試:

  1. 起點與終點相同:-b 天安門西 天安門西 -map subway.txt -o routine.txt

    • 顯示提示信息:起點與終點相同
  2. 起點或終點不存在: -b 天安門西 杭州東站 -map subway.txt -o routine.txt

    • 顯示提示信息:起點或終點不存在
  3. 查詢線路不存在: -a 20號線 -map subway.txt -o station.txt

    • 顯示提示信息:線路不存在
  4. 命令行參數格式錯誤: -f 天安門西 天安門西 -map subway.txt -o routine.txt

    • 顯示提示信息:驗證參數格式
相關文章
相關標籤/搜索