CHNTuring

导航

2019软件工程——地铁个人项目总结

GitHub

CHNTuring/metro

 

需求回顾

实现类似百度地图等应用的公共交通换乘查询功能,要求为北京地铁换乘,考虑沿途经过站点最少为最高优先级

 

开发环境

Windows10操作系统,java version "1.8.0_221",开发工具eclipse

 

项目效果预览

 

 Java类表

说明
metroUtil 项目入口
ReaderTxt 将含有站点和线路信息的.txt文件读取并存入数据结构
Station 地铁站类
Subway Java UI地铁系统,将数据结构转化为图
Dijkstra 迪杰斯特拉算法寻找合理的换乘方案

 

 .txt文件格式

从左至右依次为线路编号,站点名称,所属线路,开通状态,是否换乘站。文件中某站点上下相邻站点若属于同一地铁线,则默认现实中互为相邻站点。

 

 程序流程

 

算法思想

地铁线路换乘查询其本质上为图的最短路径问题,在本项目中可以理解为寻找无向图起始节点与目的节点之间的最短路径,并记录路径输出在UI中。

 

1:有效数据读取,即读取开通的站点及其所属线路,存储为类ReaderTxt 的静态属性Map<String, List<Station>> map

void readFileContent(String fileName) 

reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
String tempStr = null;
int staId = 0;//站点编号,唯一
int time = 0;
while ((tempStr = reader.readLine()) != null) {// 判断是否读到文件末尾
  if (time == 0) {// 如果读取的是第一条数据则初始化站点列表对象
    time++;
    line_stations = new LinkedList<>();
  }
  String[] message = tempStr.split("\\s+");// 分割站点信息
  lines.add(message[2]);// 读地铁线路
  if (message[3].equals("1")) {// 读取开通的地铁站
    // 读取同一条线路的站点,读完则更新map并重新创建新线路的站点列表
    if (!allStations.isEmpty()
        && !((Station) ((LinkedList) allStations).getLast()).getLine().equals(message[2])) {
        map.put(((Station) ((LinkedList) allStations).getLast()).getLine(), line_stations);
        line_stations = new LinkedList<>();
    }
    Station station = new Station(staId++, message[1], message[2], message[4].equals("1"));
    allStations.add(station);
    line_stations.add(station);
  }
}

reader.close();

 

2:数据预处理,考虑北京地铁线路存在环线的特殊情况,即起始站点和终点站为同一站点,则需要将原来存储在map中的数据信息去重,同时更新站点编号(唯一),存储所有站点信息到List<Station> allStations ,初始化int型矩阵graph用于存储站点间的连通情况,根据环线首尾站点去重信息更新矩阵graph实现环线首尾连通

 void initialize() 

具体实现见Github,不重点阐述

 

3:换乘查询,其核心算法为迪杰斯特拉算法,由Dijkstra类实现,迪杰斯特拉算法思想不作为重点阐述

 Dijkstra类属性

/* 属性 */

private Queue visited = null;// 已访问站点

int[] distance = null;;// 路径长度

private Map<String, List<Station>> line_stations = null;

static String nowLine = null;// 当前地铁线

static String[] nextLine = null;// 当前节点的下一换乘地铁线

static HashMap path = null;// 路径

static List<Station> list = null;

 

初始化路径长度数组distance

// 路径HashMap path;
path = new HashMap();
for (int i = 0; i < list.size(); i++)
    path.put(i, "");

// 初始化路径长度数组distance
for (int i = 0; i < list.size(); i++) {
    path.put(i, path.get(i) + "" + list.get(v).getStaName());
    if (i == v)
       distance[i] = 0;
    // 连通站点
    else if (weight[v][i] != -1) {
       distance[i] = weight[v][i];
       // 获取当前站点所属的地铁线
       nowLine = list.get(v).getLine();
       StringBuffer sbf = new StringBuffer();
       for (Station s : line_stations.get(nowLine)) {
           sbf.append(s.getStaName());
       }
       // 起点站和下一站点是否属于同一地铁线
       if (sbf.indexOf(list.get(i).getStaName()) != -1) {
          path.put(i, path.get(i) + "\n\t-->" + list.get(i).getStaName());
          nextLine[i] = nowLine;
       } else {
          path.put(i, path.get(i) + "\n-->换乘" + list.get(i).getLine() + "\n\t-->" + list.get(i).getStaName());
          nextLine[i] = list.get(i).getLine();
       }
    }
    // 不连通
    else
        distance[i] = Integer.MAX_VALUE;
}
visited.add(v);

 

迭代寻找最优路径

//迭代寻找最优线路
while (visited.size() < list.size()) {
    int k = getIndex(visited, distance);// 获取未访问点中距离源点最近的点
    visited.add(k);
    if (k != -1) {

        for (int j = 0; j < list.size(); j++) {
            if (weight[k][j] != -1)// 判断k点能够直接到达的点
            {
                // 通过遍历各点,比较是否有比当前更短的路径,有的话,则更新distance,并更新path。
                if (distance[j] > distance[k] + weight[k][j]) {
                    distance[j] = distance[k] + weight[k][j];

                    nowLine = nextLine[k];
                    StringBuffer sbf = new StringBuffer();
                    for (Station s : line_stations.get(nowLine)) {
                        sbf.append(s.getStaName());
                    }
                    //判断到下一站点是否需要换乘
                    if (sbf.indexOf(list.get(j).getStaName()) != -1) {
                        path.put(j, path.get(k) + "\n\t-->" + list.get(j).getStaName());
                        nextLine[j] = nowLine;
                    } else {
                        StringBuffer tmpSbf = new StringBuffer();

                        for (String str : line_stations.keySet()) {
                            tmpSbf = new StringBuffer();
                            for (Station s : line_stations.get(str)) {
                                tmpSbf.append(s.getStaName());
                            }
                            if (tmpSbf.indexOf(list.get(j).getStaName()) != -1
                                    && tmpSbf.indexOf(list.get(k).getStaName()) != -1) {
                                path.put(j,
                                        path.get(k) + "\n-->换乘" + str + "\n\t-->" + list.get(j).getStaName());
                                nextLine[j] = str;
                            }
                        }
                    }
                }
            }
        }
    }
}

 

个人总结

  地铁项目是软件工程课的个人项目,一开始以为可以很容易实现,结果花了不少时间来构思合理的数据结构来存储线路和站点信息,完成所有功能模块后发现其实代码和流程可以更进一步优化,终版代码的复杂度其实是受到了一开始的.txt数据文件的束缚,读取.txt文件需要处理地铁线和站点的之间的站点和所属线路、相邻站点之间的约束关系,代码量和复杂度就相应的增大了不少,通过这次实战,涨了不少经验值,开发初期如果综合考虑.txt数据的存储形式或者数据处理方式,后期可以节省很大的时间精力和开销来编写可读性和可优化性更好的代码。

  根据现实情况算法流程其实可以优化,在本项目开发过程中着重考虑的是经过站点最少的线路为最优线路,但现实中一般人们出行都是选择换乘最少的地铁线路,可以考虑能否能够以换乘最少为考虑的最高优先级,沿线经过站点较少次之,课余可以尝试在原基础上挑战一下。

 

posted on 2019-10-15 13:08  31701010贺洁伟  阅读(337)  评论(0编辑  收藏  举报