个人项目-地铁路线规划系统设计及实现

个人博客

GitHub项目地址

问题重述

问题需要实现两个功能:

  1. 输入地铁线路名称,显示整条地铁线路站点
  2. 输入两个地铁站名称,并为其规划最短线路

由于地铁数据并未给出,所以收集北京市地铁线路数据,自行设计数据存储方式。

解决思路

  1. 收集数据并设计数据存储方式(之前已经给出)
  2. 读入数据并构建地铁图的数据结构
  3. 设计并确定寻找最短路径算法
  4. 使用测试案例对程序进行debug测试

具体实现

1.数据储存形式

由于json格式数据较为简单易读,所以本项目使用json格式储存数据,具体形式如下

{
    "lines": [
    { "lineNo":1,"lineName":"1号线" , "stations":[
        {"stationName":"苹果园"},
        {"stationName":"古城"},
        {"stationName":"八角游乐园"},
       ....
    ] },
    { "lineNo":2,"lineName":"2号线" , "stations":[
        {"stationName":"积水潭"},
        {"stationName":"鼓楼大街"},
    ...
        {"stationName":"西直门"},
        {"stationName":"积水潭"}
    ] },
 .....
    ]
}

2.地铁数据结构确定

由于地铁各站点和线路之间为类似图的关系,每个节点之间有边存在,所以毫无疑问我们使用图这种数据结构对地铁线路图进行重建。具体实现如下

public class SubwayGraph {
    public ArrayList<Station> V=new ArrayList<>();
    public HashSet<Edge> edges=new HashSet<>();

    public ArrayList<Station> getV() {
        return V;
    }
    public int findStation(String stationName){
        for(int i=0;i<V.size();i++){
            if (V.get(i).getStationName().equals(stationName))return i;
        }
        return -1;
    }
    public void addV(String stationName,int lineNo,int mark) {//mark 0代表不可换乘,1代表可换乘
        int index=-1;
        if (mark==1){
            index=findStation(stationName);
        }
        if (index==-1){
            V.add(new Station(stationName));
        }else {

        }
    }

    public HashSet<Edge> getEdges() {
        return edges;
    }

    public void addEdge(int u,int v) {
        this.edges.add(new Edge(u,v));
    }
    public int isInV(Station station){
        int i;
        for ( i=0;i<V.size();i++){
            if (V.get(i).getStationName().equals(station.getStationName()))return i;
        }
        V.add(station);
        return i;
    }

}

public class Vertex {

    private int id; // 顶点的标识
    private LinkedList<Vertex> neighbors; // 这个顶点的邻居顶点
    private boolean isSerched; // 这个顶点是否被搜索过了
    private Vertex predecessor; // 这个顶点的前驱,用来记录路径的

    public Vertex(int id) {
        this.id = id;
        this.neighbors = new LinkedList<>();
        this.predecessor=null;
    }

    public void addNeighbor(Vertex... vertexes) {
        for (Vertex vertex : vertexes) {
            this.neighbors.add(vertex);
        }
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public LinkedList<Vertex> getNeighbors() {
        return neighbors;
    }

    public void setNeighbors(LinkedList<Vertex> neighbors) {
        this.neighbors = neighbors;
    }

    public boolean isSerched() {
        return isSerched;
    }

    public void setSerched(boolean isSerched) {
        this.isSerched = isSerched;
    }

    public Vertex getPredecessor() {
        return predecessor;
    }

    public void setPredecessor(Vertex predecessor) {
        this.predecessor = predecessor;
    }

}

3.最短路径算法的设计

在本次作业中,我并没有使用dijkstra算法,而是使用了广度优先遍历(BFS)来寻找最短路径。这样虽然没有dijkstra算法那么完善,但是足以满足寻找两点之间的最短值。本次作业是将两站点之间的距离换乘都不再考虑范围之内,简化了题目的难度。bfs算法具体实现如下

/*这里具体编码出现一点小失误,dij应为dijkstra算法缩写却是bfs算法*/
 public ArrayList<Station> DIJ(SubwayGraph graph, int starta, int enda) {
        Vertex[] vertices = new Vertex[graph.V.size()];
        ArrayList<Station> plan=new ArrayList<>();
        for (int i = 0; i < vertices.length; i++) {
            vertices[i] = new Vertex(i);
        }
        for (Edge i : graph.edges) {
            vertices[i.getU()].addNeighbor(vertices[i.getV()]);
            vertices[i.getV()].addNeighbor(vertices[i.getU()]);
        }
        Vertex start = vertices[starta];
        Vertex end = vertices[enda];
        boolean hasPath = bfs(start, end);
        if (hasPath) {
            // print path
            Vertex predecessor = end;
            do {
                plan.add(graph.getV().get(predecessor.getId()));
                predecessor = predecessor.getPredecessor();
            } while (predecessor != null);
        } else {
            System.out.println("不存在该起点到该终点的路径");
        }
        return plan;

    }

    public static boolean bfs(Vertex start, Vertex end) {
        LinkedList<Vertex> queue = new LinkedList<>();
        queue.addLast(start); // 添加到队尾
        while (!queue.isEmpty()) {
            Vertex vertex = queue.pollFirst(); // 从队首取出一个元素
            // 如果这个顶点是否已经检索过了,则跳过
            if (vertex.isSerched()) {
                continue;
            }

            if (vertex == end) {
                // 如果到了终点了就可以返回了
                return true;
            } else {
                // 如果还不是终点,这把该顶点可以到达的邻居全部添加到队列末端
                for (Vertex neighbor : vertex.getNeighbors()) {
                    if (neighbor.getPredecessor() == null && neighbor != start) {
                        neighbor.setPredecessor(vertex);
                    }
                    queue.addLast(neighbor);
                }
            }
            vertex.setSerched(true);
        }
        return false;
    }

4.使用测试案例对程序进行debug测试

输出结果第一行统计一共经过几个站点
下面输出经过站点和换乘信息

测试案例1:(有换乘)

java -classpath fastjson-1.2.47.jar subway -b 木樨地 前门 -map subway.json -o routine.txt

结果

7
木樨地
南礼士路
复兴门
西单
4号线大兴线
宣武门
2号线
和平门
前门

测试案例2:(无换乘)

java -classpath fastjson-1.2.47.jar subway -b 木樨地 天安门西 -map subway.json -o routine.txt

结果

5
木樨地
南礼士路
复兴门
西单
天安门西

5.个人总结

在这个项目中使用了命令行输入输出,核心算法为BFS算法计算最短路径,该项目简单复习了java项目的基本流程。