北京地铁线路推荐项目
一、项目介绍
Github:github.com/LinXS597/BeijingSubway
二、需求分析
- 设计一个存储线路信息的文件
- 实现地铁线路信息的获取
- 实现基础查询功能,查询指定地铁线路经过的站点
- 实现出发地到目的地最短路径的查询
三、主要功能模块介绍
序号 | 模块名称 | 主要功能 | 对应Java文件 |
---|---|---|---|
1 | 主模块 | 流程控制、参数解析 | src/cn/edu/zucc/Main.java |
2 | 核心算法模块 | 实现Dijkstra算法 | src/cn/edu/zucc/core/DijkstraUtil.java |
3 | 输入输出模块 | 读出和写入txt文件数据 | src/cn/edu/zucc/data/FileManager.java |
4 | 存储模块 | 存储读入的站点与线路信息 | src/cn/edu/zucc/model/Station.java |
存储输出的推荐线路信息 | src/cn/edu/zucc/model/Routine.java |
1、主模块:流程控制、参数解析
支持采用参数 -map 作为标志,来获取对应的自定义地铁文件,例如:
java subway -map subway.txt
支持采用参数 -a 来指定地铁线路,采用参数 -o 来输出到指定文件station.txt,例如:
java subway -a 1号线 -map subway.txt -o station.txt
支持采用参数 -b 来指定出发地与目的地,例如:
java subway -b 天安门西 北京大学东门 -map subway.txt -o routine.txt
2、核心算法模块:实现Dijkstra算法
利用 HashMap 存储全部的站点信息,并以站点名称为key
public static HashMap
allStation = new HashMap<>(); - 执行算法前先对数据进行预处理操作,主要目的有三个:
- 遍历各条线路信息,将station去除重复后加入allStation之中,便于后续算法执行
- 根据传入参数,确定并设置routine的起点与终点
- 对一些特殊情况进行处理,如起点终点重复、起点不存在、终点不存在等
具体代码如下
public static Routine getRoutine ( String begin,String end) {
Routine routine = new Routine();
List lineStationlist = null;
Station station = null;
Station repeatstation = null;
for(String key:FileManager.subwayLineinfo.keySet()){ //遍历各条线路信息,将station去除重后加入allStation之中
lineStationlist = FileManager.subwayLineinfo.get(key);
for(int i = 0;i
- 执行Dijkstra算法,主要有以下几步:
- 定义起点到其他顶点最短距离distance,定义某个指定站点的前一个站点path,并进行初始化
HashMap
,Integer> distance = new HashMap<>();
HashMap
path = new HashMap<>(); - 选择V,求的一个从起点到V的最短路径
- 修改从起点到任一顶点W可达的最短路径长度,以及W的父结点
- 重复2、3,求得最短距离
- 定义起点到其他顶点最短距离distance,定义某个指定站点的前一个站点path,并进行初始化
具体代码如下:
public Routine Dijkstra_algorithm ( Routine routine ) {
HashMap,Integer> distance = new HashMap<>(); //储存各站点之间的最短距离,以Integer为单位,表示站点数
HashMap collected = new HashMap<>(); //判断该Station是否被访问过
HashMap path = new HashMap<>(); //存储某个指定站点的前一个站点
HashMap disitem = new HashMap<>();
Station item;
Station V;
for (String key :allStation.keySet()){ //初始化distance、collected与path
item = allStation.get(key);
collected.put(item,new Integer(0));
if (!routine.getBeginStation().equals(item)){
if (routine.getBeginStation().getLinkStations().contains(item)){ //若与起点相邻,则将距离设置为1,并将对应的path设置为起点
disitem = new HashMap<>();
disitem.put(routine.getBeginStation(),item);
distance.put(disitem,new Integer(1));
path.put(item,routine.getBeginStation());
}
else{
disitem = new HashMap<>(); //若未与起点相邻,则将初值设置为10000
disitem.put(routine.getBeginStation(),item);
distance.put(disitem,new Integer(10000));
}
}
else{
disitem = new HashMap<>();
disitem.put(routine.getBeginStation(),item);
distance.put(disitem,new Integer(0));
}
}
collected.put(routine.getBeginStation(),1);
while (true){
V = FindMinDist(routine,distance,collected); //取未被访问顶点中distance最小者
if (V.getStationName().equals("-1")) //若这样的V不存在,算法结束
break;
collected.put(V,1);
for (String key:allStation.keySet()){ //遍历每个站点
if (V.getLinkStations().contains(allStation.get(key))&&collected.get(allStation.get(key))==0){
if (distance.get(getFromtoFin(routine,V))+1
3、输入输出模块与存储模块:读出和写入txt文件数据
- READ_FILE 和 WRITE_FILE 分别用于存储读入文件与输出文件路径信息
public static String READ_FILE;
public static String WRITE_FILE;
利用HashMap存储线路数据
public static HashMap
> subwayLineinfo = new HashMap<>(); 存储站点信息的Station类设计
private String stationName; //站点名称
private String lineName; //线路名称
private List linkStations = new ArrayList<>(); //相邻站点
- 存储最短路径信息的Rountine类设计
private Station beginStation; //出发站点
private Station endStation; //结束站点
private List passStations = new ArrayList<>(); //最短路径上的站点
- 读入地铁线路与站点数据
subway.txt 文件设计如下:
1号线 站点1 站点2 ...
2号线 站点1 站点2 ...
3号线 站点1 站点2 ...
...
- 写入指定地铁线路的站点信息
station.txt 文件设计如下:
1号线
站点1
站点2
站点3
...
- 写入指定站点之间的最短路径上的站点信息
routine.txt 文件设计如下:
11
天安门西
西单
复兴门
--->换乘地铁--<2号线>--
阜成门
车公庄
西直门
--->换乘地铁--<13号线>--
大钟寺
知春路
--->换乘地铁--<10号线>--
知春里
海淀黄庄
--->换乘地铁--<4号线大兴线>--
中关村
北京大学东门
写入时需判断相邻的两个站点是否在同一条线路上,如果不在,则需更新线路信息并写入routine.txt中
public static String getLineNmae(Station station1,Station station2){ //判断两个站点是否在同一条线路上
String res = null;
List item;
for (String key : subwayLineinfo.keySet()){
item = subwayLineinfo.get(key);
if (item.contains(station1)&&item.contains(station2)){
return key; //如果是,返回线路名称,否则,返回null
}
}
return res;
}
四、测试分析
1) 重要功能测试
读入文件,命令行参数为:-map subway.txt
- 测试结果:成功
查询线路,命令行参数为:-a 1号线 -map subway.txt -o station.txt
- 测试结果:成功
2) 核心功能测试
查询最短线路,命令行参数为:-b 天安门西 北京大学东门 -map subway.txt -o routine.txt
- 测试结果:成功
3) 异常情况处理测试:
起点与终点相同:-b 天安门西 天安门西 -map subway.txt -o routine.txt
- 显示提示信息:起点与终点相同
起点或终点不存在: -b 天安门西 杭州东站 -map subway.txt -o routine.txt
- 显示提示信息:起点或终点不存在
查询线路不存在: -a 20号线 -map subway.txt -o station.txt
- 显示提示信息:线路不存在
命令行参数格式错误: -f 天安门西 天安门西 -map subway.txt -o routine.txt
- 显示提示信息:验证参数格式