layout: post
title: "地铁出行规划"
date: 2019-10-07 16:20
comments: false
tags:
软工
计划书详情可见
具体思路
为了保证每一个名字都能映射到一个同一个站点上,我用Hashmap的映射功能来确保每一个名字所对应的站点是同一个。之后只要用简单的BFS操作就可以了,我觉得这次题目最难的便是怎么存储数据,bfs倒是挺简单的。
代码测试
为了保证代码的容错性,我添加了一些特判来保证代码不会再遇到错误的时候直接报一大串的error,而是报出错误的提示,让使用者根据此进行修改。例如:
- 当参数为
-map subwaysubway.txt
时,结果为
read errors: java.io.FileNotFoundException: subwaysubway.txt (No such file or directory)
。 - 当参数为
-a 100000号线 -map subway.txt -o station.txt
时,结果为
北京地铁线路中不存在'100000号线'线路
。 - 当参数为
-b 南京南站 南京北站 -map subway.txt -o routine.txt
时,结果为
北京地铁线路中不存在'南京南站'站点 北京地铁线路中不存在'南京北站'站点
成功运行截图如下
- 需求1参数为
-map subway.txt
时,结果为
- 需求2参数为
-a 1号线 -map subway.txt -o station.txt
时,结果为
- 需求3参数为
-b 苹果园 大兴机场 -map subway.txt -o routine.txt
时,结果为
思路详情:
subway.txt(读入文件)内容如下
1号线 苹果园 古城 八角游乐园 八宝山 玉泉路 五棵松 万寿路 公主坟 军事博物馆 木樨地 南礼士路 复兴门 西单 天安门西 天安门东 王府井 东单 建国门 永安里 国贸 大望路 四惠 四惠东
2号线 积水潭 鼓楼大街 安定门 雍和宫 东直门 东四十条 朝阳门 建国门 北京站 崇文门 前门 和平门 宣武门 长椿街 复兴门 阜成门 车公庄 西直门 积水潭
4号线大兴线 安河桥北 北宫门 西苑 圆明园 北京大学东门 中关村 海淀黄庄 人民大学 魏公村 国家图书馆 动物园 西直门 新街口 平安里 西四 灵境胡同 西单 宣武门 菜市口 陶然亭 北京南站 马家堡 角门西 公益西桥 新宫 西红门 高米店北 高米店南 枣园 清源路 黄村西大街 黄村火车站 义和庄 生物医药基地 天宫院
5号线 天通苑北 天通苑 天通苑南 立水桥 立水桥南 北苑路北 大屯路东 惠新西街北口 惠新西街南口 和平西桥 和平里北桥 雍和宫 北新桥 张自忠路 东四 灯市口 东单 崇文门 磁器口 天坛东门 蒲黄榆 刘家窑 宋家庄
6号线 金安桥 杨庄 西黄村 廖公庄 田村 海淀五路居 慈寿寺 花园桥 白石桥南 车公庄西 车公庄 平安里 北海北 南锣鼓巷 东四 朝阳门 东大桥 呼家楼 金台路 十里堡 青年路 褡裢坡 黄渠 常营 草房 物资学院路 通州北关 北运河西 北运河东 郝家府 东夏园 潞城
7号线 北京西站 湾子 达官营 广安门内 菜市口 虎坊桥 珠市口 桥湾 磁器口 广渠门内 广渠门外 九龙山 大郊亭 百子湾 化工 南楼梓庄 欢乐谷景区 垡头 双合 焦化厂
8号线 朱辛庄 育知路 平西府 回龙观东大街 霍营 育新 西小口 永泰庄 林萃桥 森林公园南门 奥林匹克公园 奥体中心 北土城 安华桥 安德里北街 鼓楼大街 什刹海 南锣鼓巷 中国美术馆
8号线南段 珠市口 天桥 永定门外 木樨园 海户屯 大红门南 和义 东高地 火箭万源 五福堂 德茂 瀛海
9号线 国家图书馆 白石桥南 白堆子 军事博物馆 北京西站 六里桥东 六里桥 七里庄 丰台东大街 丰台南路 科怡路 丰台科技园 郭公庄
10号线 巴沟 苏州街 海淀黄庄 知春里 知春路 西土城 牡丹园 健德门 北土城 安贞门 惠新西街南口 芍药居 太阳宫 三元桥 亮马桥 农业展览馆 团结湖 呼家楼 金台夕照 国贸 双井 劲松 潘家园 十里河 分钟寺 成寿寺 宋家庄 石榴庄 大红门 角门东 角门西 草桥 纪家庙 首经贸 丰台站 泥洼 西局 六里桥 莲花桥 公主坟 西钓鱼台 慈寿寺 车道沟 长春桥 火器营 巴沟
13号线 西直门 大钟寺 知春路 五道口 上地 西二旗 龙泽 回龙观 霍营 立水桥 北苑 望京西 芍药居 光熙门 柳芳 东直门
14号线西段 张郭庄 园博园 大瓦窑 郭庄子 大井 七里庄 西局
14号线东段 北京南站 永定门外 景泰 蒲黄榆 方庄 十里河 北工大西门 平乐园 九龙山 大望路 金台路 朝阳公园 枣营 东风北桥 将台 望京南 阜通 望京 东湖渠 来广营 善各庄
15号线 清华东路西口 六道口 北沙滩 奥林匹克公园 安立路 大屯路东 关庄 望京西 望京 望京东 崔各庄 马泉营 孙河 国展 花梨坎 后沙峪 南法信 石门 顺义 俸伯
16号线 北安河 温阳路 稻香湖路 屯佃 永丰 永丰南 西北旺 马连洼 农大南路 西苑
八通线 四惠 四惠东 高碑店 传媒大学 双桥 管庄八里桥 通州北苑 果园 九棵树 梨园 临河里 北桥
昌平线 昌平西山口 十三陵景区 昌平 昌平东关 北邵洼 南邵 沙河高教区 沙河 巩华城 朱辛庄 生命科学园 西二旗
亦庄线 宋家庄 肖村 小红门 旧宫 亦庄桥 亦庄文化园 万源街 容京东街 荣昌东街 同济南路 经海路 次渠南 次渠 亦庄火车站
房山线 郭公庄 大葆台 稻田 长阳 篱笆房 广阳城 良乡大学城北 良乡大学城 良乡大学城西 良乡南关 苏庄 阎村东
机场线 东直门 三元桥 T2/T3航站楼
s1线 石厂 小园 栗园庄 上岸 桥户营 四道桥 金安桥
燕房线 燕山 房山城关 饶乐府 马各庄 大石河东 星城 阎村 紫草坞 阎村东
西郊线 香山 植物园 万安 茶棚 颐和园西门 巴沟
新机场线 草桥 大兴新城 大兴机场
首先,每一行代表了一条线路,第一个字符串代表了当前线路的名称。在他后面的字符串则代表了当前线路的站点的名称,字符串的前后顺序代表了站点的前后顺序。
建立了一个关于站点的类叫做station。
name代表站点的名称;visited代表是否访问过当前的站点,用于BFS计算最短路径;preStation变量用于记录访问这一个站点之前的站点,用于通过计算返回最短路径具体通过的站点。
class Station {
String name;
boolean visited;
String preStation;
List lineNow = new ArrayList();
List nextStation = new ArrayList();
}
建立了一个关于线路的类叫做line。
id代表了当前线路的编号;name代表了当前线路的名称;stations用于记录当前线路中存在的站点的,其中他们序列号的顺序就是他们在线路中的顺序。
class Line {
int id;
String name;
List stations = new ArrayList();
}
初始化一些参数
lines用于保存所有的线路;map用于通过映射来使得每一个name都能映射到同一个station类中,保证station之间不会有冲突。
static List lines = new ArrayList();
static HashMap map = new HashMap<>();
建立getSubwayMessage方法
通过读取subway.txt来保存线路和站点的信息,并根据上面的存储方式进行存储。
public void getSubwayMessage(String fileIn) {
try {
int cnt = 1;
String path = fileIn;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(path)),"UTF-8"));
String getLine = null;
while ((getLine = bufferedReader.readLine()) != null) {
Line line = new Line();
String[] list = getLine.split(" ");
line.id = cnt;
line.name = list[0];
for(int i = 1; i < list.length-1; i++) {
Station station1 = new Station();
Station station2 = new Station();
if(map.containsKey(list[i])) {
station1 = map.get(list[i]);
map.remove(list[i]);
} else {
station1.name = list[i];
station1.visited = false;
}
if(map.containsKey(list[i+1])) {
station2 = map.get(list[i+1]);
map.remove(list[i+1]);
} else {
station2.name = list[i+1];
station2.visited = false;
}
if(!station1.lineNow.contains(line.name)) {
station1.lineNow.add(line.name);
}
if(!station2.lineNow.contains(line.name)) {
station2.lineNow.add(line.name);
}
if(!station1.nextStation.contains(station2)) {
station1.nextStation.add(station2);
}
if(!station2.nextStation.contains(station1)) {
station2.nextStation.add(station1);
}
station1.preStation = station1.name;
station2.preStation = station2.name;
map.put(list[i], station1);
map.put(list[i+1], station2);
if (!line.stations.contains(station1.name)) {
line.stations.add(station1.name);
}
if (!line.stations.contains(station2.name)) {
line.stations.add(station2.name);
}
}
lines.add(line);
cnt++;
}
bufferedReader.close();
} catch (Exception e) {
System.err.println("read errors: " + e);
}
return ;
}
建立getStationByLine方法。
通过遍历lines中的内容来输出当前线路的所有站点。
public void getStationByLine(String name, String fileOut) {
List ans = new ArrayList();
for (Line line : lines) {
if (line.name.equals(name)) {
ans = line.stations;
break;
}
}
try {
FileWriter fileWriter = new FileWriter(fileOut);
int index = 0;
for (String station : ans) {
if (index == 0) {
fileWriter.write(station);
index = 1;
} else {
fileWriter.write("->" + station);
}
}
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println();
return;
}
建立BFS方法
通过使用队列的方法来实现BFS算法,并通过preStation保存路径、
public void BFS(String st, String ed) {
for (Map.Entry entry : map.entrySet()) {
entry.getValue().visited = false;
}
Queue queue = new LinkedList<>();
queue.add(st);
while(!queue.isEmpty()) {
String now = queue.poll();
map.get(now).visited = true;
if(now.equals(ed)) {
break;
}
for(Station station : map.get(now).nextStation) {
if(map.get(station.name).visited==false) {
map.get(station.name).preStation = now;
queue.add(station.name);
}
}
}
}
建立printPath方法。
通过设置的preStation可以输出经过的所有站点。然后再通过判断路径中的线路换乘,保证换乘的次数相对较少。
public void printPath(String st, String ed, String fileOut) {
List list = new ArrayList<>();
String now = ed;
int flag = 0;
String preLine = "";
while(!now.equals(st)) {
list.add(now);
now = map.get(now).preStation;
}
Collections.reverse(list);
try {
FileWriter fileWriter = new FileWriter(fileOut);
fileWriter.write(list.size() + "\r\n");
fileWriter.write(st);
for(int i = 0; i < list.size(); i++) {
flag = 0;
if(map.get(list.get(i)).lineNow.size()==1) {
fileWriter.write("->" + list.get(i));
} else {
int j;
for(j = i+1; j < list.size(); j++) {
if(map.get(list.get(j)).lineNow.size()==1) {
if(!map.get(list.get(i)).lineNow.get(0).contains(map.get(list.get(j)).lineNow.get(0))) {
if(preLine.equals(map.get(list.get(j)).lineNow.get(0))) {
fileWriter.write("->" + list.get(i));
} else {
fileWriter.write("\r\n");
fileWriter.write(map.get(list.get(j)).lineNow.get(0) + "\r\n");
preLine = map.get(list.get(j)).lineNow.get(0);
fileWriter.write(list.get(i));
}
}
break;
}
}
}
}
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
建立main方法
通过处理传入的args参数,来判断调用了哪些方法,总共有以下几种情况:
- 如果传入了
-map
参数,那么我们可以认为之后的那一个参数就是需要读入的文件名; - 如果传入了
-a
参数,那么我们可以认为之后的那一个参数就是需要读入的线路名; - 如果传入了
-o
参数,那么我们可以认为之后的那一个参数就是输出文件的文件名; - 如果传入了
-b
参数,那么我们可以认为之后传入的两个参数依次是起始站点和结束站点;
当解决了参数的问题后,只要根据不同的情况进行不同的方法调用就可以了。
public static void main(String[] args) {
Solve solve = new Solve();
String fileIn = "";
String fileOut = "";
String line = "";
String start = "";
String end = "";
for(int i = 0; i < args.length; i++){
if(args[i].equals("-map")) {
i++;
fileIn = args[i];
}
if(args[i].equals("-a")) {
i++;
line = args[i];
}
if(args[i].equals("-o")) {
i++;
fileOut = args[i];
}
if(args[i].equals("-b")) {
i++;
start = args[i];
i++;
end = args[i];
}
}
if(args.length==2) {
solve.getSubwayMessage(fileIn);
System.out.println("读入的地铁信息如下:");
for(Line line1 : lines) {
System.out.print(line1.name + ":");
for(String s : line1.stations) {
System.out.print(" " + s);
}
System.out.println();
}
} else if(args.length==6) {
solve.getSubwayMessage(fileIn);
int flag = 0;
for(Line line1 : lines) {
if(line1.name.equals(line)) {
flag = 1;
}
}
if(flag==1) {
solve.getStationByLine(line, fileOut);
System.out.println("Successfully output all site results on the route to " + fileOut);
} else {
System.out.println("北京地铁线路中不存在'" + line + "'线路");
}
} else if(args.length==7) {
solve.getSubwayMessage(fileIn);
int flag1 = 0;
int flag2 = 0;
for(Line line1 : lines) {
if (line1.stations.contains(start)) {
flag1 = 1;
}
}
for(Line line1 : lines) {
if (line1.stations.contains(end)) {
flag2 = 1;
}
}
if(flag1==1 && flag2==1) {
solve.BFS(start, end);
solve.printPath(start, end, fileOut);
}
if(flag1==0) {
System.out.println("北京地铁线路中不存在'" + start + "'站点");
}
if(flag2==0) {
System.out.println("北京地铁线路中不存在'" + end + "'站点");
}
}
}