地铁线路最短路径问题
项目介绍
主要功能
提供一副地铁线路图,计算指定两站之间最短(最少经过站数)乘车路线;输出指定地铁线路的所有站点。以北京地铁为例,地铁线路信息保存在data.txt中,格式如下:
地铁线路总数
线路名1 站名1 站名2 站名3 ...
线路名2 站名1 站名2 站名3 ...
线路名3 站名1 站名2 站名3 ...
...
github
https://github.com/xupppp/subwayAssistant
实现语言
java
主要算法
迪杰斯特拉(dijkstra)
类职责划分
类名 | 职责 |
---|---|
Subway.java | 程序的入口,处理参数和输出结果 |
Station.java | model,存储站点信息 |
result.java | model,计算最短路径时存储结果,作用类似于dist数组 |
DataBuilder.java | 读入地铁线路图信息并存储在数据结构中 |
SubwayUtil.java | 程序用到的算法 |
核心代码
public class Station {
private String name; //站点名
private List<String> line=new ArrayList<String>(); //所在线路(换乘站有多条)
private List<Station> linkStations = new ArrayList<Station>(); //与之相邻的站点
//这里没有放get&set和构造器的代码
}
public class Result {
private Station star; //起始站
private Station end; //终点站
private int distance; //距离
private Station passStations; //到达该站的最短路径中的上一站
private String line; //到达该站在几号线上
private int linechange; //标记从上一站到该站是否有换乘,0为无换乘,1为需换乘
//这里没有放get&set和构造器的代码
}
public class DataBuilder {
public static LinkedHashSet<List<Station>> lineSet = new LinkedHashSet<List<Station>>(); //线路集合
public DataBuilder(String pathname) throws IOException{
File filename = new File(pathname);
InputStreamReader reader = new InputStreamReader( new FileInputStream(filename));
BufferedReader br = new BufferedReader(reader);
String content="";
content=br.readLine(); //读取第一行,获取共有几条线路
int linenum=Integer.parseInt(content);
for(int i=0;i<linenum;i++) { //循环往lineSet中添加line
content=br.readLine();
List<Station> line=new ArrayList<Station>();
String[] linearr=content.split(" ");
String linename=linearr[0];
for(int j=1;j<linearr.length;j++) { //循环往line中添加station
int flag=0;
for(List<Station> l:lineSet) { //处理换乘站
for(int k=0;k<l.size();k++) {
if(l.get(k).getName().equals(linearr[j])) {
List<String> newline=l.get(k).getLine();
newline.add(linename);
l.get(k).setLine(newline);
line.add(l.get(k));
flag=1;
break;
}
}
if(flag==1)
break;
}
if(j==linearr.length-1&&linearr[j].equals(linearr[1])) { //处理环线
line.get(0).getLinkStations().add(line.get(line.size()-1));
line.get(line.size()-1).getLinkStations().add(line.get(0));
flag=1;
}
if(flag==0)
line.add(new Station(linearr[j],linename));
}
for(int j=0;j<line.size();j++) { //初始化每个车站相邻的车站
List<Station> newlinkStations=line.get(j).getLinkStations();
if(j==0) {
newlinkStations.add(line.get(j+1));
line.get(j).setLinkStations(newlinkStations);
}
else if(j==line.size()-1) {
newlinkStations.add(line.get(j-1));
line.get(j).setLinkStations(newlinkStations);
}
else {
newlinkStations.add(line.get(j+1));
newlinkStations.add(line.get(j-1));
line.get(j).setLinkStations(newlinkStations);
}
}
lineSet.add(line);
}
br.close();
}
}
public class SubwayUtil {
private static List<Station> analysisList = new ArrayList<>(); //已经分析过的站点
private static HashMap<Station, Result> resultMap = new HashMap<>(); //结果集
private static Station getNextStation() { //获取下一个需要分析的站点
int min=999999;
Station rets = null;
Set<Station> stations = resultMap.keySet();
for (Station station : stations) {
if (analysisList.contains(station)) {
continue;
}
Result result = resultMap.get(station);
if (result.getDistance() < min) {
min = result.getDistance();
rets = result.getEnd();
}
}
return rets;
}
private static List<String> getSameLine(List<String> l1,List<String> l2) { //获取l1和l2中相同的线路
List<String> sameline=new ArrayList<String>();
for(String la:l1) {
for(String lb:l2) {
if(la.equals(lb))
sameline.add(la);
}
}
return sameline;
}
public static Result shortestPath(Station star, Station end) { //dijkstra计算最短路径
for(List<Station> l:DataBuilder.lineSet) { //构造结果集
for(int k=0;k<l.size();k++) {
Result result = new Result();
result.setStar(star);
result.setEnd(l.get(k));
result.setDistance(999999);
result.setLinechange(0);
resultMap.put(l.get(k), result);
}
}
for(Station s:star.getLinkStations()) { //初始化结果集
resultMap.get(s).setDistance(1);
resultMap.get(s).setPassStations(star);
List<String> samelines=getSameLine(star.getLine(),s.getLine());
resultMap.get(s).setLine(samelines.get(0));
}
resultMap.get(star).setDistance(0);
analysisList.add(star);
Station nextstation = getNextStation(); //计算下一个需要分析的站点
while(nextstation!=null) { //循环计算每一个站点的最短路径
for(Station s:nextstation.getLinkStations()) {
if(resultMap.get(nextstation).getDistance()+1<resultMap.get(s).getDistance()) { //更新最短路径
resultMap.get(s).setDistance(resultMap.get(nextstation).getDistance()+1);
resultMap.get(s).setPassStations(nextstation);
List<String> samelines=getSameLine(nextstation.getLine(),s.getLine());
if(!samelines.contains(resultMap.get(nextstation).getLine())) { //需要换乘
resultMap.get(s).setLine(samelines.get(0));
resultMap.get(s).setLinechange(1);
}
else {
resultMap.get(s).setLine(resultMap.get(nextstation).getLine());
}
}
}
analysisList.add(nextstation);
nextstation = getNextStation();
}
return resultMap.get(end);
}
public static List<Station> getLineStation(String linename){ //获取指定路线的所有站点
int flag=1;
for (List<Station> l:DataBuilder.lineSet) {
flag=1;
for(Station s :l) {
if(!s.getLine().contains(linename))
flag=0;
}
if(flag==1)
return l;
}
return null;
}
public static List<String> getPath(Result r){ //根据result生成乘车路线
List<String> path=new ArrayList<String>();
Stack<Station> tmp=new Stack<Station>();
Station s=r.getPassStations();
while(!s.equals(r.getStar())) {
tmp.push(s);
s=resultMap.get(s).getPassStations();
}
path.add(r.getStar().getName());
while(!tmp.empty()) {
if(resultMap.get(tmp.peek()).getLinechange()==1) {
path.add("->换乘"+resultMap.get(tmp.peek()).getLine());
path.add(tmp.pop().getName());
}
else
path.add(tmp.pop().getName());
}
if(r.getLinechange()==1) {
path.add("->换乘"+r.getLine());
path.add(r.getEnd().getName());
}
else
path.add(r.getEnd().getName());
return path;
}
}
测试用例
- 获得1号线的所有站点
运行参数:-a 1号线 -map data.txt -o result.txt
输出:
苹果园 古城 八角游乐园 八宝山 玉泉路 五棵松 万寿路 公主坟 军事博物馆 木樨路 南礼士路 复兴门 西单 天安门西 天安门东 王府井 东单 建国门 永安里 国贸 大望路 四惠 四惠东
- 获得2号线的所有站点(环线)
运行参数:-a 2号线 -map data.txt -o result.txt
输出:
西直门 积水潭 鼓楼大街 安定门 雍和宫 东直门 东四十条 朝阳门 建国门 北京站 崇文门 前门 和平门 宣武门 长椿街 复兴门 阜成门 车公庄 --该线路为环线--
- 从天通苑到花园桥(单线站点到单线站点)
运行参数:-b 天通苑 花园桥 -map data.txt -o result.txt
输出:
共经过16站
天通苑
天通苑南
立水桥
->换乘13号线
霍营
回龙观
龙泽
西二旗
上地
五道口
知春路
大钟寺
西直门
->换乘2号线
车公庄
->换乘6号线
车公庄西
白石桥南
花园桥
- 从奥林匹克公园到东直门(换乘站到换乘站)
运行参数:-b 奥林匹克公园 东直门 -map data.txt -o result.txt
输出:
共经过9站
奥林匹克公园
奥林中心
北土城
安华桥
安德里北街
鼓楼大街
->换乘2号线
安定门
雍和宫
东直门
- 从巴沟到长春桥(环线)
运行参数:-b 巴沟 长春桥 -map data.txt -o result.txt
输出:
共经过3站
巴沟
火器营
长春桥
异常处理
- 查找不存在的地铁线路
运行参数:-a 20号线 -map data.txt -o result.txt
输出:
不存在该地铁线路
- 输入的起始站或终点站不存在
运行参数:-b 西直门 龙翔桥 -map data.txt -o result.txt
输出:
输入的站点名不存在