地铁线路最短路径问题

项目介绍

主要功能


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

    输入的站点名不存在

原文地址:https://www.cnblogs.com/xupppp/p/11663525.html

时间: 2024-10-30 21:34:34

地铁线路最短路径问题的相关文章

天津地铁线路最短路径计算项目规划

天津地铁线路路径查询项目规划 一.项目介绍 实现一个帮助进行地铁出行路线规划的命令行程序. 二.项目完成预估表 PSP 2.1 Personal Software Process Stages Time Time Planning 计划 · Estimate · 估计这个任务需要多少时间 1day Development 开发 · Analysis · 需求分析 (包括学习新技术) 3day · Design Spec · 生成设计文档 1day · Design Review · 设计复审 (

个人项目1(地铁线路最短路径的程序)初步分析

1.需求分析 根据个人项目简介,软件的需求有以下三个: 需求1: 实现一个支持显示地铁线路与计算换乘的程序.之后,用户可以通过命令行启动这个程序.程序在启动时,会读取不同命令对应的命令行参数.对于地铁线路信息图,我们约定它采用参数 -map 作为标志.程序启动时需要通过读取 -map 参数来获得对应的自定义地铁文件(命名为 subway.txt),从而得到地铁线路图的信息. 需求2: 实现基础的查询操作.比如说,用户希望查询指定地铁线经过的站点.这样,在应用程序需要支持一个新的命令行参数 -a,

地铁线路问题分析

一.任务: 实现一个帮助进行地铁出行路线规划的命令行程序,能处理正确输入的命令行的计算地铁线路最短路径. 二.设计: 输入格式:选择json格式来输入,便于阅读. 需求1:显示地铁线路信息 将地铁线路信息等用一个文本文件以 subway.txt的形式保存起来,应保存的信息应包括地铁线路名称.各个地铁站点的名称以及车站换乘信息,使得应用程序可以通过读取这个文件,就能掌握关于北京地铁线路的所有信息. java subway -map subway.txt 需求2:查询指定路线经过的站点 查询指定地铁

个人项目--北京地铁线路规划(已完成)

一.任务: 实现一个帮助进行北京地铁出行路线规划的命令行程序. 二.需求分析及实现 需求1 在程序启动时,自动获取到地图信息 需要实现一个支持自动加载subway.txt 文件的程序 需求2 查询指定地铁线经过的站点 在应用程序上,需要支持一个新的命令行参数  -a ,指定用户希望查询的地铁线路. 在给定地铁线路时,程序需要从线路的起始站点开始,依次输出该地铁线经过的所有站点,直到终点站.输出的文件使用-o参数来指定. 一个调用应用程序的示例如下: java subway -a 1号线 -map

石家庄地铁线路查询系统

石家庄地铁线路查询系统开发: 合作人:李玉超 数据库的设计为:建立了一张表,有line_id(路线号).stop_id(站号).stop_name(站名).change(某站可换乘的线号)这几列. stop_id所显示的序号首位也可代表line_id,后两位为该站在其所在线路上的一个顺序排序序号,可以体现其位置. 设计思想: 将所有的站点可分为两类:一种是只在一条线路上(普通点),一种是可在两条线路上,即为两条线路交点(换乘点). 所以可以分为3种情况: ①:起始点:普通点   终点:普通点 ②

地铁线路项目-结对编程

地铁线路项目-结对编程 2019.7.25 完成了模块开发:填写PSP实际完成时间:完成了模块设计:进行单元测试.黑盒测试. PSP表记录: PSP2.1 Personal Software Process Stages Time Real Time Planning 计划 1h 1h ??·?Estimate ?? ·?估计这个任务需要多少时间 1h 1h Development 开发 21h 20.5h ??·?Analysis ?? ·?需求分析?(包括学习新技术) 2h 1.5h ??·

地铁线路项目设计与分析

#地铁线路项目设计与分析 ##一.项目介绍 #####实现一个帮助进行地铁出行路线规划的命令行程序 ##二.项目计划表 | PSP2.1 | Personal Software Process Stages | Time | | --- | --- | --- | | Planning | 计划 | | · Estimate | · 估计这个任务需要多少时间 | 2h | Development | 开发| | · Analysis | · 需求分析 (包括学习新技术) | 6h | · Des

地铁线路项目

设计需求 1.思考并设计一个简明易懂,可灵活扩张,方便读取的文件格式在文本文件中存储地铁信息 2.实现一个支持显示地铁线路及相关信息与计算换乘的程序 3.实现查询指定地铁线路,指定地铁站点信息等基础查询操作 4.当用户输入两个站点时,显示两个站点之间的最短线路和换乘信息,并将线路信息写入文本文件记录下来 5.设计的软件对于各中各样的出错情况要尽可能进行精确报错 6.测试代码并优化 实现思路 地铁站的相关信息有线路,站点名称,是否开通,是否换乘. 线路编号 站点名称 是否开通 是否换乘 1 刘园

地铁线路项目简析

一.需求分析 依据日常使用来看,地铁线路问题有三个需求:    1.能显示地铁所有的线路.站点信息    2.找出指定地铁线路的所有地铁站    3.找出两个地铁站间最短路径包含换乘信息‘ 二.设计思路 (一)编程语言 本次编程决定使用Java语言来进行,因为比起C语言,Java语言更能通过外接函数库来实现一个软件应有的功能. (二)编程思路 通过构造一个类来保存站点信息 List<Station> linkStations; 可以选择通过Floyd算法或者Dijkstra算法,找到最短路径后