1. 程式人生 > 實用技巧 >地鐵最短路徑完整

地鐵最短路徑完整

專案綜述

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

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

1、需求分析

  • 支援查詢指定地鐵線路的所有站點

  • 支援查詢任意兩站之間最短乘車路線,包含經過站名

  • 能夠輸出換乘站點、換乘站數、乘車站數

  • 能夠在短時間內響應使用者,輸出資訊

2、實現語言

Java

3、實現演算法

Floyd

演算法介紹

弗洛伊德演算法定義了兩個二維矩陣:

  1. 矩陣D記錄頂點間的最小路徑
    例如D[0][3]= 10,說明頂點0 到 3 的最短路徑為10;
  2. 矩陣P記錄頂點間最小路徑中的中轉點
    例如P[0][3]= 1 說明,0 到 3的最短路徑軌跡為:0 -> 1 -> 3。

它通過3重迴圈,k為中轉點,v為起點,w為終點,迴圈比較D[v][w] 和 D[v][k] + D[k][w] 最小值,如果D[v][k] + D[k][w] 為更小值,則把D[v][k] + D[k][w] 覆蓋儲存在D[v][w]中。

下圖能夠幫助理解概念。(參考https://blog.csdn.net/jeffleo/article/details/53349825,講解的很好,很容易理解)

4、類職責劃分

  1. Beanstation
        private
    String stationname;//站點名 private List<String> stations=new ArrayList<String>();//某線路中的站點 public String getStationname() { return stationname; } public void setStationname(String stationname) { this.stationname = stationname; } public List<String> getStations() {
    return stations; } public void setStations(List<String> stations) { this.stations = stations; }
  2. lineprocess-處理線路和站點資訊,建立領接領接,呼叫floyd-核心演算法,見核心程式碼部分
    public lineprocess(List<G> vertices)//初始化領接矩陣函式
    public void edg(G start, G stop, int weight)//新增站點之間的邊函式
    public void floyd(int[][] Graph)//核心,floyd演算法
    public StringBuffer output(G start, G stop,int[][] sub,List<Beanstation> lines)//查詢最短路徑並輸出函式
  3. readtxt-讀入文字資料
  4. start-UI入口
  5. FrmMain-UI頁面

5、核心程式碼

構造線路圖

    private static final int MAX= 999;
    private int[][] matirx;//地鐵線路圖的鄰接矩陣
    public List<G> vertex;//頂點
    public int[][] getMatirx() {
        return matirx;
    }

    //初始化鄰接矩陣
    public lineprocess(List<G> vertices) {
        this.vertex = vertices;
        int size = this.vertex.size();
        this.matirx = new int[size][size];
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                if(i==j) this.matirx[i][j]=0;
                else  this.matirx[i][j]=MAX;
            }
        }
    }
    public void edg(G start, G stop, int a) {//站點間的邊邊
        int i = vertex.indexOf(start);
        int j = vertex.indexOf(stop);
        int n = matirx.length;
        if (i >= 0 && i < n && j >= 0 && j < n&& this.matirx[i][j] == MAX && i != j) {
            this.matirx[i][j] = a;
            this.matirx[j][i] = a;
        }
    }
 
  • floyd演算法-核心
    private int[][] D = null;//頂點間的最小路徑值矩陣
    private int[][] P = null;//對應點的最小路徑的前驅點,例如p(1,3) = 2 說明頂點1到頂點3的最小路徑要經過2 
    private int[][][] path = null;
    private int[] QAQ =null;
    public void floyd(int[][] Graph) {
        int vexnum = Graph.length;//頂點數
        this.D = Graph;//初始化D矩陣
        this.P = new int[vexnum][vexnum];
        this.QAQ = new int[vexnum];
        this.path = new int[vexnum][vexnum][];
        //初始化P矩陣 
        for (int v = 0; v < vexnum; v++) {
            QAQ[v] = -1;
            for (int w = 0; w < vexnum; w++)
            P[v][w] =-1;
        }        
        //這裡是弗洛伊德演算法的核心部分
         //k為中間點 
        for (int k = 0; k < vexnum; k++) {
            //v為起點 
            for (int v = 0; v < vexnum; v++) {
                //w為終點
                for (int w = 0; w < vexnum; w++) {
                    if (D[v][w] > D[v][k] + D[k][w]) {
                        D[v][w] = D[v][k] + D[k][w];//更新最小路徑
                        P[v][w] = k;//更新最小路徑中間頂點 
                    }
                }
            }
        }
        //v->w的路徑
        for (int v = 0; v < vexnum; v++) {
            int[] lenth = new int[1];//經過的點數
            for (int w = 0; w < vexnum; w++) {
                lenth[0] = 0;
                //起點為v
                QAQ[lenth[0]++] = v;
                resultpath(P, v, w, QAQ, lenth);// 更新QAQ
                path[v][w] = new int[lenth[0]];
                for (int s = 0; s < lenth[0]; s++)
                    path[v][w][s] = QAQ[s];
            }
        }
    }
    // 輸出v到w的路徑
    private void resultpath(int[][] P, int v, int w, int[] QAQ, int[] lenth) {
        if (v == w) return ;
        if (P[v][w] == -1)
            QAQ[lenth[0]++] = w;
        else {
            resultpath(P, v, P[v][w], QAQ, lenth);
            resultpath(P, P[v][w], w, QAQ, lenth);
        }
    }
  • 讀取資料
public class readtxt {
    public List<Beanstation> txt(String data) throws IOException{
    List<Beanstation> lines=new ArrayList<Beanstation>();//存取所有線路
    String fg=" ";//空格是分隔符
    File file=new File(data);
    InputStreamReader read = new InputStreamReader(new FileInputStream(file));
    BufferedReader bufferedReader = new BufferedReader(read);
    String Line1 = null;
    //讀檔案
    while((Line1 = bufferedReader.readLine()) != null){       
    // 字串分隔
    Beanstation station=new Beanstation();
    String route="";    
    String tmp[] = Line1.split(fg);
    route=tmp[0];
    station.setStationname(route);
    List<String> stations=new ArrayList<>();//站點集合
    for(String s:tmp) stations.add(s);
    stations.remove(0);
    station.setStations(stations);
    lines.add(station);//在該線路中加站點
    }
    return lines;
    }
}

程式碼詳見https://github.com/31803160/subway

6、測試用例

  1. 初始頁面(預設顯示1號線站點)
  2. 檢視某路線中的所有站點
  3. 任意兩點線路查詢
  4. 同一條線
  5. 輸入為空
  6. 起點終點相同
  7. 輸入站點不存在

7、個人總結

瞭解了寫一個小專案的大致流程以及如何呈現在部落格上。

學習了Floyd演算法,CSDN上有很多講解的很好的部落格非常值得學習。

認清自己,適當放棄。