地铁线路最短路径

1.主要功能:

提供一副地铁线路图,计算指定两站之间最短(最少经过站数)乘车路线;输出指定地铁线路的所有站点。

2.具体实例:

以北京地铁为例,地铁线路信息如下图片:

站点信息格式为:

地铁线路总数
线路1 站名1 站名2 ... 站名n
线路2 站名1 站名2 ... 站名n
线路n 站名1 站名2 ... 站名n

3.实现功能:

通过一定的规则录入地图信息
查询两个地点之间存在的路线
为用户提供一个两个地点之间最短的路径(默认任何两个站点之间距离相等)

4.实现语言:java(IDE:eclipse)

5.实现算法:

Dijkstra算法

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。
它是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
Dijkstra算法算是贪心思想实现的。首先把起点到所有点的距离存下来找到最近的,然后遍历一遍把刚刚找到的距离最近的点作为中转站来看,其他点会不会更离得近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
在此不考虑实际中各个站点间的距离,默认每个站点之间的距离为1。

Dijkstra算法详解:https://blog.csdn.net/lbperfect123/article/details/84281300

6.类职责划分
1.地铁站点信息Station:

private String name; //地铁站名称,地铁站名称在所有站点中唯一且不相同,作为标识信息

public Station prev; //前一个站

public Station next; //后一个站

public Station (String name){
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

//本站到某一个目标站所经过的所有站
private Map<Station,LinkedHashSet> orderSetMap= new HashMap<Station,LinkedHashSet>();
//staion储存目的站点,LinkedHashset储存到目的站点之间经过的所有站点,表示某个站到一个目标站和所经过的所有站集合

//获取到目标站的所有站点
public LinkedHashSet getAllPassedStations(Station station) {
if(orderSetMap.get(station) == null){
LinkedHashSet set = new LinkedHashSet();
set.add(this);
resource.put(station, set);
}
return orderSetMap.get(station);
}

public Map<Station, LinkedHashSet> getOrderSetMap() {
return orderSetMap;
}

public boolean equals(Object obj) {//判断两个站点信息是否属于同一个站点
if(this == obj){
return true;
} else if(obj instanceof Station){
Station s = (Station) obj;//用Station类型的主属性name进行判断
if(s.getName().equals(this.getName())){
return true;
}
else {
return false;
}
}
else {
return false;
}
}

2.地铁线路的信息储存格式:

//直接输入地铁线路信息
//1号线
String line1Str = "苹果园 古城 八角游乐园 八宝山 玉泉路 五棵松 万寿路 公主坟 军事博物馆 木樨路 南礼士路 复兴门 西单 天安门西 天安门东 王府井 东单 建国门 永安里 国贸 大望路 四惠 四惠东";
String[] line1Arr = line1Str.split(" ");
for(String s : line1Arr){
line1.add(new Station(s));//把字符串中的站点创建为Station类型储存在该线路中
}
for(int i =0;i<line1.size();i++){
if(i<line1.size()-1){
line1.get(i).next = line1.get(i+1);//设置地铁线路前后站点顺序
line1.get(i+1).prev = line1.get(i);//设置地铁线路前后站点顺序
}
}
//2号线
String line2Str = "西直门 积水潭 鼓楼大街 安定门 雍和宫 东直门 东四十条 朝阳门 建国门 北京站 崇文门 前门 和平门 宣武门 长椿街 复兴门 阜成门 车公庄 西直门";
String[] line2Arr = line2Str.split(" ");
for(String s : line2Arr){
line2.add(new Station(s));
}
for(int i =0;i<line2.size();i++){
if(i<line2.size()-1){
line2.get(i).next = line2.get(i+1);
line2.get(i+1).prev = line2.get(i);
}
}
类似这样子的直接存储地铁站点信息

7.核心代码

//获取参数station直接相连的所有站
private List getAllLinkedStations(Station station){
List linkedStaions = new ArrayList();
for(List line : Lines.lineSet){
if(line.contains(station)){
Station s = line.get(line.indexOf(station));
if(s.prev != null){
linkedStaions.add(s.prev);//加入该站点的前一站
}
if(s.next != null){
linkedStaions.add(s.next);//加入该站点的后一站
}
}
}
return linkedStaions;
}

//计算某个起点站到各个站的最短距离,距离x站,则距离为x
private Station getShortestPath(Station station){
int minPatn = Integer.MAX_VALUE;
Station rets = null;
for(Station s :station.getOrderSetMap().keySet()){
if(outList.contains(s)){
continue;
}
LinkedHashSet set = station.getAllPassedStations(s);//参数station到s所经过的所有站点的集合
if(set.size() < minPatn){
minPatn = set.size();
rets = s;
}
}
return rets;
}

//计算从七点站到目的站的最短路线
public void calculate(Station s1,Station s2){
if(outList.size() == Lines.totalStaion){
//输出起点站到目标站点的查询结果
System.out.println("找到目标站点:"+s2.getName()+",共经过"+(s1.getAllPassedStations(s2).size()-1)+"站");
for(Station station : s1.getAllPassedStations(s2)){
System.out.print(station.getName()+"->");
}
return;
}
if(!outList.contains(s1)){
outList.add(s1);
}
//如果起点站的OrderSetMap为空,则第一次用起点站的前后站点初始化之
if(s1.getOrderSetMap().isEmpty()){
List Linkedstations = getAllLinkedStations(s1);
for(Station s : Linkedstations){
s1.getAllPassedStations(s).add(s);
}
}
Station parent = getShortestPath(s1);//获取距离起点站s1最近的一个站(
if(parent == s2){
System.out.println("找到目标站点:"+s2+",共经过"+(s1.getAllPassedStations(s2).size()-1)+"站");
for(Station station : s1.getAllPassedStations(s2)){
System.out.print(station.getName()+"->");
}
return;
}
for(Station child : getAllLinkedStations(parent)){
if(outList.contains(child)){
continue;
}
int shortestPath = (s1.getAllPassedStations(parent).size()-1) + 1;
if(s1.getAllPassedStations(child).contains(child)){
//如果起始站s1已经计算过到此站点的经过距离,那么比较计算出最小的距离
if((s1.getAllPassedStations(child).size()-1) > shortestPath){

s1.getAllPassedStations(child).clear();
s1.getAllPassedStations(child).addAll(s1.getAllPassedStations(parent));
s1.getAllPassedStations(child).add(child);
}
} else {
//如果起始站s1还没有计算过到此站点的经过距离
s1.getAllPassedStations(child).addAll(s1.getAllPassedStations(parent));
s1.getAllPassedStations(child).add(child);
}
}
outList.add(parent);
calculate(s1,s2);//重复计算
}
private void getLines(String s) {//非常非常原始的选择方法,只列举出了一部分,后续线路和列举出的部分相似
switch(s)
{
case"1":
System.out.println("地铁1号线: ");
for(Station station : Lines.line1){
System.out.print(station.getName()+"->");
}
case"2":
System.out.println("地铁2号线: ");
for(Station station : Lines.line2){
System.out.print(station.getName()+"->");
}
case"4":
System.out.println("地铁4号线: ");
for(Station station : Lines.line4){
System.out.print(station.getName()+"->");
}
case"5":
System.out.println("地铁5号线: ");
for(Station station : Lines.line5){
System.out.print(station.getName()+"->");
}
case"6":
System.out.println("地铁6号线: ");
for(Station station : Lines.line6){
System.out.print(station.getName()+"->");
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入要提供的服务:(1:获取两个站点最佳路线 2:查询地铁线路信息) ");
double a = sc.nextDouble();
if(a==1)
{
//键盘输入起始站与结束站
System.out.print("请输入起始站: ");
String s1 = sc.next();
System.out.print("请输入目的站: ");
String s2 = sc.next();

Subway k = new Subway();
k.calculate(new Station(s1), new Station(s2));
System.out.println();
}
else if(a==2) {
System.out.print("请输入要查询的地铁线路: ");
String s = sc.next();
Subway k = new Subway();
k.getLines(s);
}
else System.out.print("输入错误 ");
}

8.测试用例

查询地铁线路站点信息:

同一条地铁线路,不需要换乘:

换乘一次:

换乘两次:

多站乘坐:

9.总结:
代码已上传至https://github.com/Kirame0/Subway-
· 此次实验的实践,我通过算法的重新学习和相关资料的参考学习,依据网络上的一些博客内容实例,进行了一定的修改,磕磕绊绊地完成了一部分的功能。这让我意识到了自己在java编程方面的不熟练和陌生,以及实战经验的可贵,我以后应该多多练习编程能力。另外,以前真的不熟悉博客的编写操作,这次也学习了一些博客编写的排版和页面设置等知识。
·在这次实验中,使用文件读取的过程中,总程序发生多次错误,查阅了资料后仍未修改成功,采用直接输入了地铁线路信息。这次的实验中,有很多功能没有很好地实现,需要继续努力。
· 除了锻炼了自己的代码实现能力之外,自己对于开发软件,完成需求以及搜索对完成相应功能需要的资料、学习这一过程也有了新的认识和体验。

posted @ 2020-10-21 21:50  桃源新雨  阅读(465)  评论(0)    收藏  举报