1. 程式人生 > 其它 >北京地鐵查詢實現最短路徑

北京地鐵查詢實現最短路徑

在做這個web的時候首先做了線路的線路名稱查詢和站點查詢,在做最短路徑的時候遇到了困難,因為在平時做的系統中並沒有涉及到太複雜的演算法,這次涉及到了演算法,我在github上找了一些資料來寫

package algorithm;

import java.util.ArrayList;
import java.util.List;

public class Station {
    private String StationName;/*記錄站點名*/
    private boolean Visited;/*是否被遍歷過*/
    private String PreStation;/*便於搜尋是返回和記錄
*/ List<String> StationLine = new ArrayList<>();/*該站點存在的所有線路資訊*/ public List<Station> NearStation = new ArrayList<>();/*該站點存在的所有鄰近站點資訊*/ public String getStationName() { return StationName; } public void setStationName(String stationName) { StationName
= stationName; } public boolean isVisited() { return Visited; } public void setVisited(boolean visited) { Visited = visited; } public String getPreStation() { return PreStation; } public void setPreStation(String preStation) { PreStation
= preStation; } public List<String> getStationLine() { return StationLine; } public void setStationLine(List<String> stationLine) { StationLine = stationLine; } public List<Station> getNearStation() { return NearStation; } public void setNearStation(List<Station> nearStation) { NearStation = nearStation; } }

 

package algorithm;

import java.util.ArrayList;

//演算法,地鐵最短路徑,資料儲存類
public class line {
    public String LineName;/*記錄當前線路名*/
    ArrayList<String> Station = new ArrayList<>();/*記錄該線路中所有的站點名*/

    public String getLineName() {
        return LineName;
    }

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

    public ArrayList<String> getStation() {
        return Station;
    }

    public void setStation(ArrayList<String> station) {
        Station = station;
    }

    @Override
    public String toString() {
        return "Line{" +
                "LineName='" + LineName + '\'' +
                ", Station=" + Station +
                '}';
    }
}

 

package algorithm;

import java.io.*;
import java.util.*;

public class Main {
    static ArrayList<line> LineList = new ArrayList<>();//存放所有線路的列表
    static ArrayList<Station> StationList = new ArrayList<>();//存放線路站點的列表
    public static HashMap<String, Station> stationHashMap = new HashMap<>();//存放對應站點的Hash
    /*對檔案進行讀入和儲存操作*/
    public static void SubwayMessage(String pathname){
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(pathname)));
            String NowLine = null;
            while ((NowLine=bufferedReader.readLine()) != null){
                line line = new line();
                String[] StationInformation = NowLine.split(" ");
                line.setLineName(StationInformation[0]);
                for (int i = 1; i < StationInformation.length-1; i++){
                    Station NowStation = new Station();
                    Station NextStation = new Station();
                    if(stationHashMap.containsKey(StationInformation[i])){
                        /*如果hashmap中已經存在該站點資訊,因為需要修改所以將它放入NowStation中*/
                        NowStation = stationHashMap.get(StationInformation[i]);
                        stationHashMap.remove(StationInformation[i]);
                    }
                    else{
                        NowStation.setStationName(StationInformation[i]);
                        NowStation.setVisited(false);
                    }
                    if(stationHashMap.containsKey(StationInformation[i+1])){
                        /*如果hashmap中已經存在該站點資訊,因為需要修改所以將它放入NowStation中*/
                        NextStation = stationHashMap.get(StationInformation[i+1]);
                        stationHashMap.remove(StationInformation[i+1]);
                    }
                    else{
                        NextStation.setStationName(StationInformation[i+1]);
                        NextStation.setVisited(false);
                    }
                    /*如果站點不包含當前線路,將線路新增至站點*/
                    if(!NowStation.getStationLine().contains(line.LineName)){
                        NowStation.getStationLine().add(line.LineName);
                    }
                    if(!NextStation.getStationLine().contains(line.LineName)){
                        NextStation.getStationLine().add(line.LineName);
                    }
                    /*如果站點不含下一站,將相鄰站新增至NextStation*/
                    if(!NowStation.getNearStation().contains(NextStation)){
                        NowStation.getNearStation().add(NextStation);
                    }
                    if(!NextStation.getNearStation().contains(NowStation)){
                        NextStation.getNearStation().add(NowStation);
                    }
                    NowStation.setPreStation(NowStation.getStationName());
                    NextStation.setPreStation(NextStation.getStationName());

                    stationHashMap.put(StationInformation[i], NowStation);
                    stationHashMap.put(StationInformation[i+1], NextStation);
                    /*將當前站點新增至線路中*/
                    if(!line.getStation().contains(NowStation.getStationName())){
                        line.getStation().add(NowStation.getStationName());
                    }
                    if(!line.getStation().contains(NextStation.getStationName())){
                        line.getStation().add(NextStation.getStationName());
                    }
                }
                LineList.add(line);
            }
            /*釋放資源*/
            bufferedReader.close();
        } catch (IOException e) {
            System.out.println("Read file error, Please try again!");
        }
    }
    /*輸出線路資訊*/
    public static void PrintMessage(String StartStation, String EndStation){
        List<String> list = new ArrayList<>();
        String NowStation = EndStation;
        String PreLine = "";
        while (!NowStation.equals(StartStation)){
            list.add(NowStation);
            NowStation = stationHashMap.get(NowStation).getPreStation();
        }
        Collections.reverse(list);
        System.out.println("當前路程共經過:"+(list.size())+"站");
        System.out.print(StartStation);
        for (int i = 0; i < list.size(); i++){
            if(stationHashMap.get(list.get(i)).getStationLine().size()==1){
                /*如果當前站點只存在一條線路,顯然不可能在此站換乘*/
                System.out.print("-->"+stationHashMap.get(list.get(i)).getStationName());
                PreLine = stationHashMap.get(list.get(i)).getStationLine().get(0);
            }
            else {
                /*如果該站點存在多條線路,並且下一站只有一條線路*/
                if (stationHashMap.get(list.get(i+1)).getStationLine().size()==1){
                    /*如果該站和前一站線路相同,直接輸出
                     * 如果不同,則表明為換乘線,輸出該站名,然後顯示換乘線路資訊*/
                    if(stationHashMap.get(list.get(i+1)).getStationLine().get(0).equals(PreLine)){
                        System.out.print("-->"+stationHashMap.get(list.get(i)).getStationName());
                    }
                    else{
                        System.out.println("-->"+stationHashMap.get(list.get(i)).getStationName());
                        System.out.println("換乘"+stationHashMap.get(list.get(i+1)).getStationLine().get(0));
                        PreLine = stationHashMap.get(list.get(i+1)).getStationLine().get(0);
                    }
                }
                else{
                    /*對於多線路站點,如果包含前一站的線路資訊說明不需要換乘
                     * 如果不包含,遍歷前後兩站的線路資訊,換乘線路一定存在於兩站都共有的線路節點中*/
                    if (stationHashMap.get(list.get(i+1)).getStationLine().contains(PreLine)){
                        System.out.print("-->"+stationHashMap.get(list.get(i)).getStationName());
                    }
                    else{
                        boolean IsSame = false;
                        for (int t1 = 0; t1 < stationHashMap.get(list.get(i)).getStationLine().size(); t1++){
                            if (stationHashMap.get(list.get(i+1)).getStationLine().contains(stationHashMap.get(list.get(i)).getStationLine().get(t1))){
                                System.out.println("-->"+stationHashMap.get(list.get(i)).getStationName());
                                System.out.println("換乘"+stationHashMap.get(list.get(i)).getStationLine().get(t1));
                                PreLine = stationHashMap.get(list.get(i)).getStationLine().get(t1);
                                IsSame = true;
                                break;
                            }
                        }
                        if(IsSame){
                            System.out.print("-->"+stationHashMap.get(list.get(i)).getStationName());
                        }
                    }
                }
            }
        }
    }
    /*利用BFS演算法進行最短路徑遍歷*/
    public static void SearchShortPath(String StartStation, String EndStation){
        /*初始化所有站點的遍歷資訊*/
        for (Map.Entry<String, Station> entry: stationHashMap.entrySet()){
            entry.getValue().setVisited(false);
        }
        Queue<String> queue = new LinkedList<>();
        queue.add(StartStation);
        while (!queue.isEmpty()){
            /*如果佇列不為空,移除佇列頭部,將該站設定為遍歷過*/
            String NowStation = queue.poll();
            stationHashMap.get(NowStation).setVisited(true);
            if (NowStation.equals(EndStation)){
                break;
            }
            /*對於該站點的所有相鄰節點進行遍歷,如果未被遍歷,則PreStation設定為當前站,將該相鄰站新增至佇列中*/
            for (Station station:stationHashMap.get(NowStation).NearStation){
                if (stationHashMap.get(station.getStationName()).isVisited()==false){
                    stationHashMap.get(station.getStationName()).setPreStation(NowStation);
                    queue.add(station.getStationName());
                }
            }
        }
    }
    /*查詢線路資訊*/
    public static void main(String[] args) {
        SubwayMessage("D:\\subway.txt");
        Scanner input = new Scanner(System.in);
        System.out.println("請輸入起始站:");
        String start = input.nextLine();
        System.out.println("請輸入終點站");
        String end = input.nextLine();
        boolean IsExisted = true;
        if (!stationHashMap.containsKey(start)) {
            IsExisted = false;
            System.out.println("不存在該起始站");

        }
        if (!stationHashMap.containsKey(end)) {
            IsExisted = false;
            System.out.println("不存在該終點站");
        }
        if (IsExisted) {
            SearchShortPath(start, end);
            PrintMessage(start, end);
        }
    }
}

這個只能在控制檯輸出,我根據這個進行改寫。

將方法的返回值改為物件或者集合,然後使用域物件進行儲存,在使用el表示式將其結果輸出。

package ShortPath.util;

import ShortPath.model.BeanEdge;
import ShortPath.model.BeanShortestPath;
import ShortPath.model.BeanVertex;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class Graph {
    private BeanEdge edges;
    private Map<String, BeanVertex> vertexes;

    /**
     * @param edges
     * @param vertexes
     */
    public Graph(BeanEdge edges, Map<String, BeanVertex> vertexes) {
        super();
        this.edges = edges;
        this.vertexes = vertexes;
    }


    public int[] dijkstra(int startVertexIndex) {
        boolean[] collected = new boolean[edges.getEdgeValues().length];
        int[] dist = new int[edges.getEdgeValues().length];
        int[] path = new int[edges.getEdgeValues().length];

        // 初始化
        for (int i=0; i<edges.getEdgeValues().length; i++) {
            if (i==startVertexIndex) {  // 等於起始點
                collected[i] = true;
                dist[i] = 0;
                path[i] = i;
            } else {  // 不是起始點
                collected[i] = false;
                if (edges.getEdgeValues()[startVertexIndex][i] > 0) {  // 起始點能到
                    dist[i] = edges.getEdgeValues()[startVertexIndex][i];
                    path[i] = startVertexIndex;
                } else {
                    dist[i] = Integer.MAX_VALUE;
                    path[i] = -1;
                }
            }

        }

        while (true) {
            int minDistIndex = getMinVertex(collected, dist);  // 未收錄頂點中 collected=false 的dist最小者
            if (minDistIndex == -1) {
                break;
            }
            collected[minDistIndex] = true;

            for (int i=0; i<edges.getEdgeValues().length; i++) {  // minVertex的每個鄰接點
                if (edges.getEdgeValues()[minDistIndex][i]>0 && collected[i] == false) {
                    dist[i] = dist[minDistIndex] + edges.getEdgeValues()[minDistIndex][i];
                    path[i] = minDistIndex;
                }
            }
        }

        return path;
    }

    private int getMinVertex(boolean[] collected, int[] dist) {
        int minDist = Integer.MAX_VALUE;
        int minDistIndex = -1;
        for (int i=0; i<edges.getEdgeValues().length; i++) {
            if (collected[i] == false && dist[i]<minDist) {
                minDist = dist[i];
                minDistIndex = i;
            }
        }

        return minDistIndex;
    }

    public BeanShortestPath getShortestPath(String startStationName, String endStationName) {
        int[] path = dijkstra(vertexes.get(startStationName).getId());

        Map<Integer, BeanVertex> id2StationName = new HashMap<>();  // 再搞一個id到結點的對映
        id2StationName = getId2StationName();

        BeanShortestPath shortestPath = new BeanShortestPath();
        int stationId = vertexes.get(endStationName).getId();
        while (stationId != vertexes.get(startStationName).getId()) {
            shortestPath.getStations().add(id2StationName.get(stationId));  // 根據id找名字找站點
            shortestPath.getLineNames().add(edges.getEdgeNames()[stationId][path[stationId]]);  // 當前站到 上一站 的名字
            stationId = path[stationId];
        }
        shortestPath.getStations().add(id2StationName.get(stationId));  // 加入起點
        Collections.reverse(shortestPath.getStations());
        Collections.reverse(shortestPath.getLineNames());
        return shortestPath;
    }

    private Map<Integer, BeanVertex> getId2StationName() {
        Map<Integer, BeanVertex> id2StationName = new HashMap<>();
        for (BeanVertex v: vertexes.values()) {
            id2StationName.put(v.getId(), v);
        }
        return id2StationName;
    }

    public Map<String, BeanVertex> getVertexes() {
        return vertexes;
    }

    public BeanEdge getEdges() {
        return edges;
    }


    public void setEdges(BeanEdge edges) {
        this.edges = edges;
    }


    public void setVertexes(Map<String, BeanVertex> vertexes) {
        this.vertexes = vertexes;
    }
}
package ShortPath.util;

import ShortPath.model.BeanEdge;
import ShortPath.model.BeanVertex;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Reader {
    public static Graph readSubwayInfo(String txtPath) {
        Graph graph = null;
        BufferedReader reader;
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(txtPath)), "UTF-8"));

            // 先將檔案中的資料讀入 (因為要重複用到)
            ArrayList<String> txtLines = new ArrayList<String>();
            {  // 使變數名txtLine只在此區域生效  便於下面for each遍歷時取同樣的變數名
                String txtLine;
                while ((txtLine = reader.readLine()) != null) {
                    txtLines.add(txtLine);
                }
            }

            // 儲存所有站點
            Map<String, BeanVertex> stations = new HashMap<>();
            for (String txtLine: txtLines) {
                String[] stationNames = txtLine.split(" ");
                String lineName = stationNames[0];
                for (int i=1; i<stationNames.length; i++) {  // 0是lineName
                    if (!stations.containsKey(stationNames[i])) {  // map中沒有 需要建立後加入
                        BeanVertex v = new BeanVertex(stations.size(), stationNames[i]);
                        v.getLine().add(lineName);
                        stations.put(stationNames[i], v);
                    } else {  // map中有 需要加一個lineName
                        stations.get(stationNames[i]).getLine().add(lineName);
                    }
                }

            }


            // 儲存邊資訊 即站點之間的聯絡  以及邊名稱(線路名稱)
            BeanEdge edge = new BeanEdge(stations.size());
            for (String txtLine: txtLines) {
                String[] stationNames = txtLine.split(" ");
                String lineName = stationNames[0];
                for (int i=1; i<stationNames.length-1; i++) {
                    BeanVertex station1 = stations.get(stationNames[i]);
                    BeanVertex station2 = stations.get(stationNames[i+1]);
                    edge.getEdgeValues()[station1.getId()][station2.getId()] = 1;
                    edge.getEdgeValues()[station2.getId()][station1.getId()] = 1;
                    edge.getEdgeNames()[station1.getId()][station2.getId()] = lineName;
                    edge.getEdgeNames()[station2.getId()][station1.getId()] = lineName;
                }
            }

            // 構建圖
            graph = new Graph(edge, stations);
        } catch (UnsupportedEncodingException | FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return graph;
    }
}
package Servlet;

import ShortPath.model.BeanShortestPath;
import ShortPath.util.Graph;
import ShortPath.util.Reader;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(name = "InputStations", value = "/InputStations")
public class InputStations extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");

        Graph graph = Reader.readSubwayInfo("D:\\subway.txt");//進行檔案匯入,如果需要匯入資料庫資料需要進行資料查詢,再將其儲存到集合中
        String start = request.getParameter("startStation");
        String end = request.getParameter("endStation");
        BeanShortestPath shortestPath = graph.getShortestPath(start,end);
        request.setAttribute("result",shortestPath);
        request.getRequestDispatcher("output.jsp").forward(request,response);
    }
}