北京地铁线路规划
代码仓库
https://github.com/Nevermoves/PekingSubwayInquir
需求分析
https://www.cnblogs.com/Javen003/p/11547788.html
模块分析
本项目将项目模块分为主模块、存储模块、IO模块和算法模块
主模块
- 启动程序并查询需要在命令行中输入参数指令
- 指令将通过正则表达式匹配判断
提供命令参数 -a
来获得某地铁线路上所有站点的信息,并通过参数 -o
将查询结果输入到指定文件
java subway -a 1号线 -map subway.txt -o station.txt
匹配的正则表达式
String line="-a \\S+ -map \\S+ -o \\S+ ";
提供命令参数 -b
来获得两个地铁站点之间d 最短线路,同样可通过参数 -o
将查询结果输入到指定文件
java subway -b 莲花桥 达官营 -map subway.txt -o routine.txt
匹配的正则表达式
String path="-b \\S+ \\S+ -map \\S+ -o \\S+ ";
存储模块
存储站点信息的Station类
stationName
存储站点名称line
存储经过该站点的轨道coon
存储在地铁线路上与该站点相临的站点
public class Station {
private String stationName;
private Setline;
private Setconn;
}
存储线路信息的Line类
lineName
存储线路名称stations
存储该线路经过的站点
public class Line {
private String lineName;
private Liststations;
}
存储图的BuildMap类
图的存储结构
- 由于计算最短是有Dijkstra算法实现的,用数字下标代替类计算将会提高写代码的效率
- 建立多种映射关系原因同上
- 图的不同结点的连接通过Station类中的
conn
实现邻接表存储
public class BuildMap {
private static Mapmap; //存储站点下标对站点的映射
private static MapnumMap; //存储站点名称对站点下标的映射
private static Setset; //存储站点名称的集合
private static Maplines; //存储线路名称对线路的映射
public static int count; //存储站点数
}
IO模块
xml文件读取
如果单纯只是为了实现实验需求,即简单的查询系统,其实可以选择使用txt文件存储地铁信息
但出于规范统一,以及可能的后续功能拓展的考虑,由于txt文件的局限性,我还是选择用xml文件格式来存储地铁信息
xml文件格式
整个城市的地铁线路存入root
中
一条地铁线路用 Line
封装,将地铁的线路名设为标签
Line
内包含多个Station
,用于存储该铁轨上各个站点的名称
苹果园
古城
八角游乐园
四惠东
xml文件读取
- 通过ReadLIne类实现xml文件的读取
- 读取方式使用了dom框架
- 将每条线路都存储在
List
中,其中第一项为线路名名称,其余皆为站点名称 - 所有的线路都存储在
set
中
public class ReadLine {
private static Set>set;
public ReadLine(String fileName) throws Exception {
File file=new File(fileName);
if(!file.exists()) {
System.out.println("文件"+fileName+"不存在.");
System.exit(0);
}
set=new HashSet>();
DocumentBuilder documentBuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document=documentBuilder.parse(file);
Element root=document.getDocumentElement();
NodeList lineList=root.getElementsByTagName("Line");
for(int i=0;istations=new ArrayList();
NamedNodeMap nameNodeMap=liNode.getAttributes();
String id=nameNodeMap.getNamedItem("id").getTextContent();
stations.add(id);
for(int j=0;j
线路查询结果存储
线路查询的结果输出文件格式
八通线
四惠
四惠东
高碑店
...
路径查询结果存储
路径查询的结果输出文件格式
5
10号线
莲花桥
六里桥
9号线
六里桥
六里桥东
...
达官营
算法模块
算法模块中主要有四个方法,分别为图的初始化,Dijkstra算法,导出并寻找最优路径
Dijkstra类说明
public class Dijkstra {
private int inf; //假象无穷大数
private int len; //存储站点数
private int startId; //存储起点的下标
private int endId; //存储终点的下标
private int[] dis; //存储所有结点到起点的距离
private int[] vis; //存储所有被访问情况
private Map>from; //存储起点到所有结点的最短路径的所有前置结点
private ListbestPath; //存储最优路径
private Listpath; //存储当前路径
}
图的初始化
- 图的初始化由方法
init
实现 - 方法的主要功能为数据的初始化,以及对起点及其周围的点预处理
- 根据起点和终点的站点名称,确定起点和终点的下标,若无该站点或是起点与终点相同,则提示并终止程序的运行
private void init(String start,String end) {
if(start.equals(end)) {
System.out.println("别浪费钱啊!");
System.exit(0);
}
inf=Integer.MAX_VALUE;
bestPath=null;
path=new ArrayList();
len=BuildMap.getMap().size();
if(BuildMap.getNumMap().containsKey(start)) {
startId=BuildMap.getNumMap().get(start);
}
else {
System.out.println("站点 "+start+" 不存在");
System.exit(0);
}
if(BuildMap.getNumMap().containsKey(end)) {
endId=BuildMap.getNumMap().get(end);
}
else {
System.out.println("站点 "+end+" 不存在");
System.exit(0);
}
from=new HashMap>();
dis=new int[len];
vis=new int[len];
for(int i=0;i();
from.put(i,set);
vis[i]=0;
dis[i]=inf;
}
dis[startId]=0;
vis[startId]=1;
Station station=BuildMap.getMap().get(startId);
for(Station s:station.getConn()) {
int conn=BuildMap.getNumMap().get(s.getStationName());
dis[conn]=1;
from.get(conn).add(startId);
}
}
Dijkstra算法
- Dijkstra算法的主要功能为计算各个结点到起点的最短距离
- 时间复杂度O(N+V) ,空间复杂度为O(N+V)
- 算法实现为每次循环寻找与起点距离最近、距离被更新且未被访问的结点,更新与其相邻的结点到起点的距离,直至找不到为止
- 每次结点A更新了结点B的距离时判断,若结点B新距离小于更新前到起点的距离,则清空
from[B]
,并将A加入from[B]
- 若结点B新距离与更新前到起点的距离相同,则将A加入
from[B]
中,使A成为B的最短路径的前置结点之一,以此得到多条最短路径
private void dijkstra() {
while(true) {
int x=-1;
int minx=inf;
for(int i=0;i
寻找最优路径
- 寻找最优路径的功能由两个方法共同实现
- 方法
getPath
的功能为将由路径的站点下标组成的List
转化为站点和线路List
- 方法
buildPath
为遍历所有的最短路径,存入List后输入方法getPath
得到路径,筛选出换成次数最少的路径,最后输出结果
public void buildPath(int now) {
path.add(0,now);
if(now==startId) {
ListnowPath=BuildMap.getPath(path);
if(bestPath==null) bestPath=nowPath;
else {
if(nowPath.size() getPath(List pathIndex){
Listpath=new ArrayList();
int len=pathIndex.size();
Station laStation=map.get(pathIndex.get(0));
Station noStation=map.get(pathIndex.get(1));
Line laLine=null;
for(int i=0;i