地铁线路最短路径问题

Posted xupppp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了地铁线路最短路径问题相关的知识,希望对你有一定的参考价值。

项目介绍

主要功能

技术图片
提供一副地铁线路图,计算指定两站之间最短(最少经过站数)乘车路线;输出指定地铁线路的所有站点。以北京地铁为例,地铁线路信息保存在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
    输出:

    输入的站点名不存在

以上是关于地铁线路最短路径问题的主要内容,如果未能解决你的问题,请参考以下文章

地铁最短路径问题

地铁线路最短路径问题

地铁线路最短路径

北京地铁最短路径实现

最短路径(图论-北京地铁线路查询)

个人项目——地铁线路的最短路径