个人项目-地铁线路规划系统
GitHub项目地址:https://github.com/ye1014239226/Subway1.git
问题描述
1.输入地铁线路的名字,显示该线路上的站点信息
2.输入起末站的站点名字,输出最优的换乘路线(经过站点数量最少)
解决思路
1.收集北京地铁的线路信息,存入txt文件中,并导入到设计的数据存储方式中去
2.读入数据并且构建地铁线路图的数据结构
3.使用迪杰斯特拉算法求最短的换乘路径
4.测试
解决过程
1.数据存储
txt的存储形式如下
线路名
站点名:站点名 距离
...
站点名:站点名 距离
*线路名-站点名,....,站点
001 苹果园:古城 1 古城:八角游乐园 1 八角游乐园:八宝山 1 八宝山:玉泉路 1 玉泉路:五棵松 1 五棵松:万寿路 1...四惠:四惠东 1 *001-苹果园,古城,八角游乐园,八宝山,玉泉路,.....,四惠东
2.代码解析
Station类
存储站点信息:站点名成,所在路线,邻接站点,以地铁站名称进行唯一区分
public class Station { private String name;//站点名称 private String line;//站点所属线路 private List<Station> linkStations = new ArrayList<>();//邻接站点 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLine() { return line; } public void setLine(String line) { this.line = line; } public List<Station> getLinkStations() { return linkStations; } public void setLinkStations(List<Station> linkStations) { this.linkStations = linkStations; } public Station(String name,String line) { this.name=name; this.line=line; } public Station(String name) { this.name=name; } public Station() { } public int hashCode() { return this.getName().hashCode(); } public String toString() { return "Station{" + "name=‘" + name +‘\‘‘ +",line=‘" + line + ‘\‘‘ + ", linkStations=" + linkStations + ‘}‘; } 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; } } }
Result类
存储运行结果
public class Result { private Station starStation;//输入起始站点 private Station endStation;//输入终点站点 private double distance=0; private List<Station> passStation = new ArrayList<>();//经过的站点 public Station getStarStation() { return starStation; } public void setStarStation(Station starStation) { this.starStation = starStation; } public Station getEndStation() { return endStation; } public void setEndStation(Station endStation) { this.endStation = endStation; } public double getDistance() { return distance; } public void setDistance(double distance) { this.distance = distance; } public List<Station> getPassStation() { return passStation; } public void setPassStation(List<Station> passStation) { this.passStation = passStation; } public Result(Station starStation,Station endStation,double distance) { this.starStation=starStation; this.endStation=endStation; this.distance=distance; } public Result() { } public String toString() { return "Result{"+"starStation="+starStation+", endStation="+endStation+",distance" + distance +", passStation=" + passStation+‘}‘; } }
Builder类
初始化数据(邻接点之间的距离),从txt读取初始化数据
public class Builder { public static String FILE_PATH; public static String WRITE_PATH; public static HashMap<String,HashMap<String,Double>> distanceMap = new HashMap<String,HashMap<String,Double>>();//存放站名,站名,距离 public static LinkedHashSet<List<Station>> lineSet = new LinkedHashSet<>();//所有线集合 public static HashMap<String,List<Station>> lineData; private Builder() { } static { Create(); } public static void Create(){ lineData = new HashMap<>(); for (List<Station> stations : lineSet) { lineData.put(stations.get(1).getLine(),stations); } } public static String getLineNameByStation(Station station){ Create(); String startname = station.getName(); for (Map.Entry<String,List<Station>> entry : lineData.entrySet()) { List<Station> stations = entry.getValue(); for (Station sta : stations){ if(sta.getName().equals(startname)){ return entry.getKey(); } } } return ""; } public static Double getDistance(String key) {//获取两个站之间的距离 return distanceMap.entrySet().stream().filter(x->x.getValue().keySet().contains(key)).findFirst().get().getValue().get(key); } public static String getLineName(String key) {//获取地铁线路名字 return distanceMap.keySet().stream().filter(x -> distanceMap.get(x).containsKey(key)).findFirst().orElse(""); } public static boolean isContains(String key){ return distanceMap.entrySet().stream().anyMatch(x->x.getValue().keySet().contains(key)); } public static ArrayList<Station> getLine(String lineStr,String lineName){ ArrayList<Station> line = new ArrayList<Station>(); String[] lineArr = lineStr.split(","); for (String s : lineArr) { line.add(new Station(s,lineName)); } return line; } public static void getLineDate(String lineName) {//输出一条线路上所有的站点 Create(); lineName=lineName.substring(0,3); List<Station> lineInfo = lineData.get(lineName); String lineStr = lineInfo.stream().map(x->x.getName()).collect(Collectors.joining(",","[","]")); System.out.print(lineStr); } public static void getPassStation(Result result){//输出最优线路需要经过的站点以及换成线路信息 Station starStation = result.getStarStation(); String starLine = getLineNameByStation(starStation); String converLine = starLine; System.out.println("起始地铁线:"+starLine); for (Station station : result.getPassStation()) { if(!converLine.equals(station.getLine())){ System.out.print("(换乘地铁线:"+station.getLine()+")"); converLine = station.getLine(); converLine = station.getLine(); } System.out.print(station.getName() + "->"); } } public static void readSubway() { File file = new File(FILE_PATH); BufferedReader reader = null; try { InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file),"UTF-8"); reader = new BufferedReader(inputStreamReader); String line = null; String lineName = "001"; distanceMap.put("001",new HashMap<>()); while ((line = reader.readLine()) != null) {//如果之前的文件为空,则不执行输出 if(line.trim().length()==1||line.trim().length()==3||line.trim().length()==2){ if(line.trim().length()==3||line.trim().length()==2){ continue; } lineName = line; if(!distanceMap.keySet().contains(line)){ distanceMap.put(line.trim(),new HashMap<>()); } }else{ if(line.trim().startsWith("*")){//当line以*开始时 String[] lineInfo = line.substring(1).split("-");//从第2个字符开始,以“-”划分成两个数组 lineSet.add(getLine(lineInfo[1].trim(),lineInfo[0].trim())); }else{//根据空格,“:”拆分字符串,存入distanceMap中 String texts[] = line.split("\t"); String key = texts[0].trim(); Double value = Double.valueOf(texts[1]); distanceMap.get(lineName).put(key,value); String other = key.split(":")[1].trim()+":"+key.split(":")[0].trim(); distanceMap.get(lineName).put(other,value); } } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); } finally { } } }
Dijkstra类
最短路径查找
寻找分析点的所有相邻点并且记录权值,选取最短距离的邻点,循环遍历,直到没有点可以遍历
public class Dijkstra { private static HashMap<Station, Result> resultMap = new HashMap<>(); private static List<Station> analysisList = new ArrayList<>();//存放已经分析过的站点(已被分析表示起始点到该点的最短路径已求出) public static Result calculate(Station star, Station end) { if (!analysisList.contains(star)) { analysisList.add(star); }//将star站点放到以及分析的站点中去 if (star.equals(end)) { Result result = new Result(); result.setDistance(0.0D); result.setEndStation(star); result.setStarStation(star); return resultMap.put(star, result); }//当star站点等于end站点,则设置result的距离为0,end站点为star站点。 if (resultMap.isEmpty()) { //当第一次调用calculate,并且起始点和终止点不同,那么resultMap为空。 List<Station> linkStations = getLinkStations(star); //把相邻站点集合中的所有站点,加入resultMap中。 for (Station station : linkStations) { Result result = new Result(); result.setStarStation(star); result.setEndStation(station); String key = star.getName() + ":" + station.getName(); Double distance = Builder.getDistance(key); result.setDistance(distance); result.getPassStation().add(station); resultMap.put(station, result); } } Station parent = getNextStation(); if (parent == null) {//如果resultMap所有点keySet被分析完了,则返回的parent为null。 Result result = new Result(); result.setDistance(0.0D); result.setStarStation(star); result.setEndStation(end); //put方法的返回值就是value值。 return resultMap.put(end, result); } //如果得到的最佳邻点与目标点相同,则直接返回最佳邻点对应的result对象。 if (parent.equals(end)) { return resultMap.get(parent); }//分析一个parent最佳点后,把它的相邻点都会加入到resultMap中,在下一次调用getNextStation获取resultMap中未被标记且距离(起始点到该station的距离)最短。 List<Station> childLinkStations = getLinkStations(parent); for (Station child : childLinkStations) { if (analysisList.contains(child)) { continue; } String key = parent.getName() + ":" + child.getName(); Double distance; distance = Builder.getDistance(key); Builder.getDistance(key); if (parent.getName().equals(child.getName())) { distance = 0.0D; } Double parentDistance = resultMap.get(parent).getDistance(); distance = doubleAdd(distance,parentDistance); List<Station> parentPassStations = resultMap.get(parent).getPassStation(); Result childResult = resultMap.get(child); if (childResult != null) { if (childResult.getDistance() > distance) { //如果通过最佳点比直接到距离小,则更新resultMap中的对应result对象。 childResult.setDistance(distance); childResult.getPassStation().clear(); childResult.getPassStation().addAll(parentPassStations); childResult.getPassStation().add(child);//路径更新为A->最佳点->child点。 } } else { //如果在resultMap中没有最佳点的相邻点,则往resultMap中添加通过最佳点(初始为起始点的最佳邻点)到达该点。 childResult = new Result(); childResult.setDistance(distance); childResult.setStarStation(star); childResult.setEndStation(child); childResult.getPassStation().addAll(parentPassStations); childResult.getPassStation().add(child); } resultMap.put(child, childResult); } analysisList.add(parent); calculate(star, end); return resultMap.get(end);//循环遍历得到最好的结果 } public static void reMake() {//清空分析点的集合以及结果Map analysisList.clear();; resultMap.clear();; } public static List<Station> getLinkStations(Station station) {//找出所有的邻接点 List<Station> linkedStaions = new ArrayList<Station>(); for (List<Station> line : Builder.lineSet) {//遍历每条地铁线 for (int i = 0; i < line.size(); i++) { if (station.equals(line.get(i))) { if (i == 0) { //如果该站点位于地铁线的起始站,则相邻站为地铁线的第二个站点(i+1), linkedStaions.add(line.get(i + 1)); } else if (i == (line.size() - 1)) {//如果该站点位于地铁线的最后一个站,则相邻站为地铁线的倒数第二个站点(i-1), linkedStaions.add(line.get(i - 1)); } else { //如果该站点位于地铁线的其余位置,则相邻站点为该站点前后位置(i-1/i+1) linkedStaions.add(line.get(i + 1)); linkedStaions.add(line.get(i - 1)); } } } } return linkedStaions; } private static Station getNextStation() {//计算最小的权重值,计算下一个需要分析的点 Double min = Double.MAX_VALUE; Station rets = null; Set<Station> stations = resultMap.keySet();//获取resultMap中的station集合 for (Station station : stations) { if (analysisList.contains(station)) {//如果该点被标记为“已被分析”,则跳过分析 continue; } //循环比较resultMap中未被标记的点,求出最短路径的result对象。 Result result = resultMap.get(station); if (result.getDistance() < min) { min = result.getDistance(); rets = result.getEndStation(); } } return rets;//返回下一个站点 } private static double doubleAdd(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); } }
test类
首先根据路径获得subway.txt,调用read()函数中的readSubway()函数读取,并存入
根据输入格式不同,选择不同的输出方式
public class test { public static void main(String[] args) { read(); System.out.println("线路:001,002,004,005,006,007,008,009,010,013,14东,14西,015,八通线,昌平线,亦庄线,房山线,机场线"); while(true) {//循环输入 write(); Dijkstra.reMake(); } } public static void read(){ Builder.FILE_PATH=System.getProperty("user.dir") + File.separator + "\\" +"subway2.txt";//获取当前项目路径 Builder.readSubway(); } public static void write() { Scanner input=new Scanner(System.in); System.out.print("输入指令:(查询地铁线路信息:-a 001号线),(查询起末站线路:-b 苹果园 玉泉路):"); String s=input.nextLine(); String[] split =s.split("\\s+"); switch(split[0]) { case "-map": if(split.length==1){ Builder.readSubway(); System.out.println("成功读取subway.txt文件"); }else{ System.out.println("重新输入"); } break; case "-a": if(split.length==2){ Builder.readSubway(); Builder.getLineDate(split[1]); }else{ System.out.println("重新输入"); } break; case "-b": if(split.length==3){ if(split[1].equals(split[2])) {//起末站相同,github中未修改 System.out.print("起末站相同,请重新输入"); } else { Builder.readSubway(); Result result = Dijkstra.calculate(new Station(split[1]), new Station(split[2])); Builder.getPassStation(result); } }else{ System.out.println("重新输入"); } break; } System.out.println(); } }
测试
1.输入地铁线路的名字,显示改线路上的站点信息
2.输入起末站,显示最优路径(经过站点数最少)
1)在一条地铁线上
2)在不同的地铁线上
3)起末站相同
4)末站不存在
问题与个人小结
本次作业,借鉴了网上的代码并做出了修改,实现了多次输入输出,但算法依旧存在问题,当输入的起站不存在时,算法会出错。
通过本次作业,初步了解并且会使用github,在一定程度上重新了解了java项目的基本流程
原文地址:https://www.cnblogs.com/zucc31701087/p/11663997.html