地鐵線路最短路徑(完整)


項目介紹:

 

 

 

提供一副地鐵線路圖,計算指定兩站之間最短(最少經過站數)乘車路線;輸出指定地鐵線路的所有站點。以北京地鐵為例,地鐵線路信息保存在data.txt中,格式如下:

地鐵線路總數

線路名1 站名1 站名2 站名3 ...

線路名2 站名1 站名2 站名3 ...

線路名3 站名1 站名2 站名3 ......

需求分析:

1、主要功能

  (1)能實現地鐵信息的導入

  (2)能夠保存各個站點的信息

  (3)輸入起始站點和目標站點后計算出指定兩站之間最短乘車路線

  (4)能輸出乘車線路經過的所有站點的信息

  (5)能夠給出站點的換乘信息

  (6)若有多個最短路線時優先選擇換乘少的線路

  (7)若有多個最優解則全部輸出

2、實現語言

  java

3、實現算法

  Dijkstra算法

 

  ①基本思想:設置一個集合S存放已經找到最短路徑的頂點,S的初始狀態只包含源點v,對vi∈V-S,假設從源點v到vi的有向邊為最短路徑。以后每求得一條最短路徑v, …, vk,就將vk加入集合S中,並將路徑v, …, vk , vi與原來的假設相比較,取路徑長度較小者為最短路徑。重復上述過程,直到集合V中全部頂點加入到集合S中。

 

4、類職責划分(相關類的功能進行描述)

  • Line類

用於保存線路信息

 List<Station> line 保存線路信息

 

 

  • Station類

用於保存站點信息,線路名稱,測試站點到某一個目標站點所經過的所有站集合

 

String name 站點名稱
String Linename 線路名稱
Station prevsation 在當前線路時,所在站點的前一個站
Station nextsation 在當前線路時,所在站點的后一個站
Map<Station,LinkedHashSet<Station>> ordermap 測試站點到某一個目標站點(key)所經過的所有站集合

 

 

 

 

 

 

 

  • ReadFile類

基本功能時讀取文件“地鐵線路信息.txt”內容,儲存到Line,Station中,保存所有線路及該線路的站點信息,站點總數

Set<List<Station>> lineSet    保存所有線路信息
totalStation 保存站點總數
readText() 函數用於讀取文件

 

 

 

 

 

  • dijkstra類

 用於計算最短路徑得出結果輸出

List<Station> getAllNearStations(Station station) 函數用於得到與傳入參數所有相鄰的站點
Station getShortPath(Station station) 函數用於得到參數到各個站的最短距離站
void result(Station s1,Station s2) 函數用於得出最終結果並輸出
void main(String[] args) 主函數用於輸入起始站點

 

 

 

 

 

 

5、核心代碼

Line類

public class Line {
    //集合保存線路信息
    public static List<Station> line = new ArrayList<Station>();

    public static List<Station> getLine() {
        return line;
    }

    public static void setLine(List<Station> line) {
        Line.line = line;
    }

}

 

Station類

public class Station {
//地鐵站名稱
private String name; 
//地鐵線路名
private String Linename; 

//在當前線路時,所在站點的前一個站
public Station prevsation; 

//在當前線路時,所在站點的后一個站
public Station nextsation; 

//測試站點到某一個目標站點(key)所經過的所有站集合
private Map<Station,LinkedHashSet<Station>> ordermap = new HashMap<Station,LinkedHashSet<Station>>();

        public String getLinename() {
            return Linename;
        }

        public void setLinename(String linename) {
            Linename = linename;
        }

    public Station (String name){
        this.name = name;
    }
 

    public Station() {
        // TODO Auto-generated constructor stub
    }

    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    //記錄當前測試站點到目標站點所經過的所有站點,並儲存
    public LinkedHashSet<Station> getAllPassedStations(Station station) {
        if(ordermap.get(station) == null){     
            LinkedHashSet<Station> set = new LinkedHashSet<Station>(); 
            set.add(this);//this為當前類的實例
            ordermap.put(station, set);
        }
        return ordermap.get(station);
    }
 
    public Map<Station, LinkedHashSet<Station>> getOrderMap() {
        return ordermap;
    }
    
    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        } else if(obj instanceof Station){
            Station s = (Station) obj;
            if(s.getName().equals(this.getName())){
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    
    @Override
    public int hashCode() {
        return this.getName().hashCode();
    }

}

 

ReadFile類

public class ReadFile {
    //集合保存所有線路及該線路的站點信息
    public static Set<List<Station>> lineSet = new HashSet<List<Station>>();
    //所有站點數
    public static int totalStation = 0;
     //文件讀取函數  
    public static void readText(){
        Station Prevstation = new Station();
        try {
            File file=new File("地鐵線路信息.txt");
            //判斷文件是否存在
            if(file.isFile() && file.exists()){ 
                InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
                BufferedReader bufferedReader = new BufferedReader(reader);
                String stationline = null;
              
                int flag = 0;
                while((stationline = bufferedReader.readLine()) != null){
                     String[] tmp = stationline.split(" ");//分割符號,將空格置空
                     List<Station> Linestations = new ArrayList<Station>();//設置一個鏈表保存線路信息
                    for(int j=1;j<tmp.length;j++) {
                        Station station = new Station();
                        station.setLinename(tmp[0]);//將每一行的第一個字符串存入線路名
                        station.setName(tmp[j]);//保存站點名稱
                        Linestations.add(station);//保存線路信息
                        totalStation++;//計算總站點數
                        if(flag != 0) {//當flag不為零時,存在前后站點
                            station.prevsation = Prevstation;
                            Prevstation.nextsation = station;
                        }
                        Prevstation = station;//記錄從零開始記錄的站點
                        flag++;
                    }
                    lineSet.add(Linestations);//添加為線路集
                }
               // 關閉文件
                reader.close();
                bufferedReader.close();
            }
            else
                System.out.println("找不到指定的文件!");
          } catch (Exception e) {
              System.out.println("文件內容出錯!");
              e.printStackTrace();
           }        
        }
     
}

 

dijkstra類

public class dijkstra {

private List<Station> List1 = new ArrayList<Station>();//保存已經遍歷過的站點

//得到與傳入參數所有相鄰的站點
    private List<Station> getAllNearStations(Station station){
        List<Station> linkedStaions = new ArrayList<Station>();
        for(List<Station> line : ReadFile.lineSet){//遍歷所有線路
            if(line.contains(station)){//當前線路中包含參數站點
                Station s = line.get(line.indexOf(station));//取出當前線路上的該站點的值
                if(s.prevsation != null){
                    linkedStaions.add(s.prevsation);//得到當前線路的該站點的前一個站點
                }
                if(s.nextsation != null){
                    linkedStaions.add(s.nextsation);//得到當前線路的該站點的后一個站點
                }
            }
        }
        return linkedStaions;
    }
    
//得到參數到各個站的最短距離每一站距離為1
        private Station getShortPath(Station station){
            int min = 10000;//定義一個較大的數用做比較
            Station rets = null;
            for(Station s :station.getOrderMap().keySet()){//遍歷當前測試站點到所有站點
                if(List1.contains(s)){
                    continue;
                }
                LinkedHashSet<Station> set  = station.getAllPassedStations(s);//當前測試站點到未遍歷站點的路徑
                if(set.size() < min){
                    min = set.size();//取路徑最短的哪一個
                    rets = s;
                }
            }
            return rets;//返回最近的這一個站點
        }
    
    //計算最短經過路徑
    public void result(Station s1,Station s2){
        int flag = 0;
        String tempname = "";
        String linename = "";
        int flag1=0;
        //遍歷所有站點,判斷輸入的站名名稱是否存在
        for(List<Station> line : ReadFile.lineSet){
            if(line.contains(s1)){
                flag1=1;
            }
        }
        if(flag1==0) {
            System.out.println("輸入站點不存在,請重新輸入!");
            return ;
        }
        
        int flag2=0;
        
        for(List<Station> line : ReadFile.lineSet){
            if(line.contains(s2)){
                flag2=1;
            }
        }
        if(flag2==0) {
            System.out.println("輸入站點不存在,請重新輸入!");
            return ;
        }
        //當所有站點都遍歷完
        if(List1.size() == ReadFile.totalStation){
            System.out.println("起點:"+s1.getName()+"  終點站:"+s2.getName()+",共經過"+(s1.getAllPassedStations(s2).size()-1)+"站");
            int p=0;
            for(Station station : s1.getAllPassedStations(s2)){//輸出遍歷s1到s2經過的路徑
                p++;
                if(flag == 0) {
                    tempname = station.getName();
                }
                else if(flag == 1) {
                    linename=station.getLinename();
                    System.out.print(station.getLinename()+tempname+"->");
                }
                else {
                    if(!station.getLinename().equals(linename)) {//換乘線路換行輸出
                        linename=station.getLinename();
                        System.out.println();
                    }
                    if(p<s1.getAllPassedStations(s2).size()-1) {
                        System.out.print(station.getLinename()+" "+station.getName()+"->");
                    }
                    else {
                        System.out.print(station.getLinename()+" "+station.getName());
                    }
                    
                }
                flag++;
            }
            return;
        }
        if(!List1.contains(s1)){//將當前測試站點儲存在已遍歷鏈表
            List1.add(s1);
        }
        //如果起點的測試站點的OrderMap為空,則第一次用該站的相鄰站點初始化
        if(s1.getOrderMap().isEmpty()){
            List<Station> Linkedstations = getAllNearStations(s1);
            for(Station s : Linkedstations){
                s1.getAllPassedStations(s).add(s);
            }
        }
        Station psation = getShortPath(s1);//獲取距離起點站s1最近的一個站
        if(psation == s2){
            System.out.println("找到目標站點:"+s2+",共經過"+(s1.getAllPassedStations(s2).size()-1)+"站");
            for(Station station : s1.getAllPassedStations(s2)){
                System.out.print(station.getName()+"->");
            }
            return;
        }
        for(Station station1 : getAllNearStations(psation)){
            if(List1.contains(station1)){
                continue;
            }
            int shortestPath = (s1.getAllPassedStations(psation).size()-1) + 1;//計算所需距離
            if(s1.getAllPassedStations(station1).contains(station1)){
                //如果s1已經計算過到此station1的經過距離,那么比較出最小的距離
                if((s1.getAllPassedStations(station1).size()-1) > shortestPath){
                    //重置S1到周圍各站的最小路徑
                    s1.getAllPassedStations(station1).clear();
                    s1.getAllPassedStations(station1).addAll(s1.getAllPassedStations(psation));
                    s1.getAllPassedStations(station1).add(station1);
                }
            } else {
                //如果s1還沒有計算過到此station1的經過距離
                s1.getAllPassedStations(station1).addAll(s1.getAllPassedStations(psation));
                s1.getAllPassedStations(station1).add(station1);
            }
        }
        List1.add(psation);
        result(s1,s2);//重復計算,往外面站點擴展
    }

 
    public static void main(String[] args) {
        dijkstra subway = new dijkstra();
        ReadFile.readText();
        Scanner input =new Scanner(System.in);
        System.out.print("請輸入起點:");
        String str1 = input.next();
        System.out.print("請輸入終點:");
        String str2 = input.next();
        
        subway.result(new Station(str1), new Station(str2));    
    }
}

 

6、測試用例

測試一:站點輸入有誤

測試二:不用換乘

測試三:不用換乘

測試四:換乘

測試五:換乘

7、總結

1.沒有完成需求分析中的選擇換乘少的路線和輸出全部最優解,要進一步完善思考。

2.寫代碼的過程中,意識到自己對於最短路徑算法的理解不透徹,完成過程中查找了很多資料。

3.認識到自己的不足要多多鍛煉努力。

 

完整代碼已上傳https://github.com/tongyao063/SubWay2.git


免責聲明!

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



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