北京地铁查询实现最短路径
在做这个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); } }