地铁最短路径
主要功能
编写一个北京地铁线路查询的程序,实现两个功能:
- 查询地铁线路
- 查询地铁最短路径

文件"地铁线路信息.txt"为数据文件,包含了北京地铁的所有线路及所有车站信息。其格式如下:
| 1号线 苹果园 古城 八角游乐园 八宝山 玉泉路 五棵松 万寿路 公主坟 军事博物馆 木樨路 南礼士路 复兴门 西单 天安门西 天安门东 王府井 东单 建国门 永安里 国贸 大望路 四惠 四惠东 |
|---|
| 2号线 西直门 积水潭 鼓楼大街 安定门 雍和宫 东直门 东四十条 朝阳门 建国门 北京站 崇文门 前门 和平门 宣武门 长椿街 复兴门 阜成门 车公庄 西直门 |
| 4号线大兴线 安河桥北 北宫门 西苑 圆明园 北京大学东门 中关村 海淀黄庄 人民大学 魏公村 国家图书馆 动物园 西直门 新街口 平安里 西四 灵境胡同 西单 宣武门 菜市口 陶然亭 北京南站 马家堡 角门西 公益西桥 新宫 西红门 高米店北 高米店南 枣园 清源路 黄村西大街 黄村火车站 义和庄 生物医药基地 天宫院 |
| …… |
说明:在数据文件中,每一行表示1条地铁线路,第一个为线路名称,其余为该线路的站点名称,都以空格为分隔符隔开。目前北京地铁共开通22条线,共405座站点(包括换成站点62座),其中1号线有23个站点。
具体需求
- 输入一条地铁线路名称,输出该条线路的路线走向。
- 输入起始站名和目的站名,输出从起始站到目的站的最短乘坐站换乘线路。
- 优先考虑换乘少的线路
- 加强程序健壮性,输入不存在的线路、站点的输出
实现语言
Java
算法实现
迪杰斯特拉(Dijkstra)算法
- 寻找与起始点相邻的的所有点并记录距离
- 选取最短距离的邻近点A
- 寻找与A点相邻的的所有点并记录或更新距离
- 选取最短距离的邻近点A'
- 循环3~4,直到遍历所有点
类职责划分
地铁线路构成了数据结构中的图(Graph),站点是节点(Node),站点之间的通行路径就是就是边(Edge)。由于所有通行路径都是双向的,所以这是一个无向图。在图中任选两点,两点之间的连续路径(Path)就是我们要查找的乘车路线。
程序共设计有7个类,设计如下图(Main类未画出)所示:

Main类 是程序的入口,处理输入和输出结果。
Station类 是存储站点信息。
Line类 存储地铁线路信息。
Path类 存储最短路径信息。
DataBuilder类 数据初始化,读入地铁线路信息并存储在数据结构中,相当于图。
DijkstraAlgorithm类 Dijkstra算法的具体实现。
Solve类 实现查询铁线路和查询地铁最短路径。
核心代码
public class Main {
public static void main(String[] args) {
String s = DataBuilder.readTxt("../地铁线路信息.txt"); //读入数据
Set<Line> alllinesSet = DataBuilder.CreatLines(s); //构造所有线路集合
System.out.print("||1.查询地铁线路||2.查询地铁最短路径||3.退出||");
Scanner input=new Scanner(System.in);
String choose = input.next();
while(true) {
if(choose.equals("1")) {
System.out.print("请输入需查询的地铁线路名:");
input=new Scanner(System.in);
String name = input.next();
System.out.print(Solve.searchLine(alllinesSet, name));
}
else if(choose.equals("2")) {
System.out.print("请输入起始站名和终点站名:");
input=new Scanner(System.in);
Station start = DijkstraAlgorithm.findStation(alllinesSet,input.next());
Station end = DijkstraAlgorithm.findStation(alllinesSet,input.next());
if(start==null||end==null) {
System.out.print("不存在起始站或终点站");
}
else {
HashMap<Station, Path> pathMap = DijkstraAlgorithm.dijkstra(alllinesSet,start);
List<String> paths = Solve.getPath(pathMap,pathMap.get(end));
for(String path:paths) {
System.out.print(path+" ");
}
}
}
else if(choose.equals("3")){
break;
}
else {
System.out.print("选择正确的选项");
}
System.out.print("\n||1.查询地铁线路||2.查询地铁最短路径||3.退出||");
input=new Scanner(System.in);
choose = input.next();
}
System.out.print("欢迎下次光临!");
}
}
public class Station {
private String name; //地铁站名称
public int istransport = 0; //是否为换乘站,可换乘为1,不可换乘为0
private List<String> LineNames; //站点所在的地铁线路集合
private List<Station> linkStations; //相邻站点集合
public Station (String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIstransport() {
return istransport;
}
public void setIstransport(int istransport) {
this.istransport = istransport;
}
public List<String> getLineNames() {
return LineNames;
}
public void setLineNames(List<String> lineNames) {
LineNames = lineNames;
}
public List<Station> getLinkStations() {
return linkStations;
}
public void setLinkStations(List<Station> linkStations) {
this.linkStations = linkStations;
}
}
public class Line {
private String name; //地铁线路名称
private List<Station> stations;//线路站点集合
private int iscircle=0; //是否为环线,若环线为1,非环线为0
public Line (String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Station> getStations() {
return stations;
}
public void setStations(List<Station> stations) {
this.stations = stations;
}
public int getIscircle() {
return iscircle;
}
public void setIscircle(int iscircle) {
this.iscircle = iscircle;
}
}
public class Path {
private Station start; //起点站
private Station end; //该站点
private int distance = 999999;//初始化距离为无穷大
private Station lastStation; //到达该站的最短路径中的上一站
private String line; //到达该站在几号线上
private int ischange=0; //标记从上一站到该站是否有换乘,0为无换乘,1为需换乘
public Station getStart() {
return start;
}
public void setStart(Station start) {
this.start = start;
}
public Station getEnd() {
return end;
}
public void setEnd(Station end) {
this.end = end;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
public Station getLastStation() {
return lastStation;
}
public void setLastStation(Station lastStation) {
this.lastStation = lastStation;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public int getIschange() {
return ischange;
}
public void setIschange(int ischange) {
this.ischange = ischange;
}
}
public class DataBuilder {
public static String readTxt(String txtPath) {
File file = new File(txtPath);
if(file.isFile() && file.exists()){
try {
FileInputStream fileInputStream = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuffer sb = new StringBuffer();
String text = null;
while((text = bufferedReader.readLine()) != null){
sb.append(text);
sb.append("\n");
}
bufferedReader.close();
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
public static Set<Line> CreatLines(String txt) {
Set<Line> alllinesSet = new HashSet<Line>();//所有线集合
String[] Lines = txt.split("\n");
for(int i=0;i<Lines.length;i++) {
String[] stationsOfLine = Lines[i].split(" ");
Line line = new Line(stationsOfLine[0]);
List<Station> stations = new ArrayList<Station>();
for(int j = 1;j < stationsOfLine.length;j++) {
Station station = new Station(stationsOfLine[j]);
List<String> LineNames = new ArrayList<String>();
List<Station> linkStations = new ArrayList<Station>();
for(Line k:alllinesSet) { //是否已经存在station
for(int p=0;p<k.getStations().size();p++) {
if((k.getStations().get(p).getName()).equals(stationsOfLine[j])) {
station = k.getStations().get(p);
LineNames = station.getLineNames();
linkStations = station.getLinkStations();
LineNames.add(line.getName());
station.setIstransport(1);;
break;
}
}
}
if(station.getIstransport() == 0) {
LineNames.add(line.getName());
}
if(j==stationsOfLine.length-1&&stationsOfLine[j].equals(stationsOfLine[1])) { //环线
line.setIscircle(1);
station = stations.get(0);
LineNames = station.getLineNames();
linkStations = station.getLinkStations();
linkStations.add(stations.get(1));
linkStations.add(stations.get(j-2));
}
station.setLineNames(LineNames);
station.setLinkStations(linkStations);
stations.add(station);
}
line.setStations(stations);
for(int j=0;j<line.getStations().size();j++) { //初始化每个车站相邻的车站
List<Station> newlinkStations=line.getStations().get(j).getLinkStations();
if(j==0) {
if(line.getIscircle()==0) {
newlinkStations.add(line.getStations().get(j+1));
line.getStations().get(j).setLinkStations(newlinkStations);
}
}
else if(j==line.getStations().size()-1) {
if(line.getIscircle()==0) {
newlinkStations.add(line.getStations().get(j-1));
line.getStations().get(j).setLinkStations(newlinkStations);
}
}
else {
newlinkStations.add(line.getStations().get(j+1));
newlinkStations.add(line.getStations().get(j-1));
line.getStations().get(j).setLinkStations(newlinkStations);
}
}
alllinesSet.add(line);
}
return alllinesSet;
}
}
public class DijkstraAlgorithm {
// 迪杰斯特拉算法实现(核心)
public static HashMap<Station, Path> dijkstra(Set<Line> alllinesSet,Station start) {
List<Station> finish = new ArrayList<>(); //已经分析过的站点
HashMap<Station, Path> pathMap = new HashMap<>(); //初始站点到某一站点的最短路径
for(Line i:alllinesSet) { //初始化
for(Station j:i.getStations()) {
if(!pathMap.containsKey(j)) {
Path path = new Path();
path.setStart(start);
path.setEnd(j);
if(j.getName().equals(start.getName())) {
path.setDistance(0);
}
pathMap.put(j, path);
}
}
}
for(Station LinkStations:start.getLinkStations()) {//查找起点临近点,设置最短路径
pathMap.get(LinkStations).setDistance(1);
pathMap.get(LinkStations).setLastStation(start);
List<String> samelines = getCommonLine(start,LinkStations);
pathMap.get(LinkStations).setLine(samelines.get(0));
}
finish.add(start);
Station nextStation = getNextStation(finish,pathMap);
while(nextStation!=null) { //循环计算每一个站点的最短路径
for(Station s:nextStation.getLinkStations()) {
if(pathMap.get(nextStation).getDistance()+1<pathMap.get(s).getDistance()) { //更新最短路径
pathMap.get(s).setDistance(pathMap.get(nextStation).getDistance()+1);
pathMap.get(s).setLastStation(nextStation);
List<String> samelines=getCommonLine(nextStation,s);
isChange(samelines,pathMap.get(nextStation));
pathMap.get(s).setIschange(isChange(samelines,pathMap.get(nextStation)));
if(pathMap.get(s).getIschange()==1) { //需要换乘
pathMap.get(s).setLine(samelines.get(0));
pathMap.get(s).setDistance(pathMap.get(s).getDistance()+1);//优先直线到达,换乘相当于多1站距离
}
else {
pathMap.get(s).setLine(pathMap.get(nextStation).getLine());
}
}
}
finish.add(nextStation);
nextStation = getNextStation(finish,pathMap);
}
return pathMap;
}
//根据站点名寻找某一站点(Station)
public static Station findStation(Set<Line> alllinesSet,String name) {
for(Line i:alllinesSet) {
for(Station j:i.getStations()) {
if(name.equals(j.getName())) {
return j;
}
}
}
return null;
}
// 找到两个站点的共有线路
public static List<String> getCommonLine(Station station1,Station station2) {
List<String> commonLine=new ArrayList<String>();
for(String i:station1.getLineNames()) {
for(String j:station2.getLineNames()) {
if(i.equals(j))
commonLine.add(i);
}
}
return commonLine;
}
// 找到下一个需要分析的站点
// 在不包含已找到最短路径的站点中搜索目前最短路径的站点
private static Station getNextStation(List<Station> finish,HashMap<Station, Path> pathMap) {
int min=999999;
Station nextstation = null;
Set<Station> stations = pathMap.keySet();
for (Station station : stations) {
if (!finish.contains(station)) {
Path path = pathMap.get(station);
if (path.getDistance() < min) {
min = path.getDistance();
nextstation = path.getEnd();
}
}
}
return nextstation;
}
// 判断是否换乘
private static int isChange(List<String> lines,Path path) {
if(lines.contains(path.getLine())) {
return 0;
}
else {
return 1;
}
}
}
public class Solve {
// 搜索某一条线的所有站点
public static String searchLine(Set<Line> alllinesSet,String name){
int flag=0;
String lineinfo = "";
for(Line k:alllinesSet) {
if(name.equals(k.getName())){
flag=1;
for(Station stm:k.getStations()) {
lineinfo+=stm.getName()+" ";
}
}
}
if(flag==0) {
lineinfo="不存在该条线路!";
}
return lineinfo;
}
// 获取地铁路径
public static List<String> getPath(HashMap<Station, Path> pathMap,Path path){
List<String> allpath=new ArrayList<String>();
Stack<Station> stations=new Stack<Station>();
Station station=path.getLastStation();
while(!station.equals(path.getStart())) {
stations.push(station);
station=pathMap.get(station).getLastStation();
}
allpath.add("->乘坐"+DijkstraAlgorithm.getCommonLine(path.getStart(),stations.peek()).get(0)+"<-\n");
allpath.add(path.getStart().getName());
while(!stations.empty()) {
if(pathMap.get(stations.peek()).getIschange()==1) {
allpath.add("\n->换乘"+pathMap.get(stations.peek()).getLine()+"<-\n");
}
allpath.add(stations.pop().getName());
}
if(path.getIschange()==1) {
allpath.add("\n->换乘"+path.getLine()+"<-\n");
}
allpath.add(path.getEnd().getName());
return allpath;
}
}
测试用例
查询地铁站线

查询最短路径
-
可直达
![]()
-
需换乘
![]()
-
少换乘测试
![]()
通过北京地铁线路图我们可以看到从公主坟到平安里真正的最短路径为公主坟 军事博物馆(换乘9号线) 白堆子 白石桥南(换乘6号线) 车公庄西 车公庄 平安里 ,6站换乘2次。而程序中所给出的线路为8站换乘1次,符合少换乘优先路线。从亮马桥到东单的真正最短路径为马良桥 三元桥 (换乘首都机场线) 东直门(换乘2号线) 东四十条 朝阳门 建国门(换乘1号线) 东单,6站换乘3次。程序中所给出的线路为8站换乘1次,符合少换乘优先路线。
健壮性测试


退出

总结
通过这次作业,我对软件工程这门课程有了一个更深刻的了解 ,以前总认为敲代码就好了,事实上,在真正敲代码前,还有很多必要的工作要做,比如需求分析,制作类图活动图等等。这些工作在以前,我会认为是浪费时间,但经历这次的作业,我发现前期工作越充分,敲代码的时间反而会越少,它大大加快了完成作业的时间,哪怕是这么一个小项目也能明显的体现出来。
除了重新认识这门课程,我还发现了自己的一些不足,比如编程能力还是有点弱,难以将需求很好的转化成代码体现出来,接下来还要继续努力啊!



浙公网安备 33010602011771号