一、前言
现实和理想的差距总是那么大,在过程中碰到的坑比预期的还多(说那么多,其实就是自己菜),过程中复习了淡忘已久的许多算法和数据结构,一边深刻的体会着自己是多菜,一边也体会到了自身一点点微小的进步,大概。
二、项目简介
实现北京地铁查询功能
附上一张北京地铁的图:
三、项目地址
https://github.com/fireworks-EX/MetroLinePlanning
四、需求分析
1、确定储存及读取地铁线路数据文件的格式
2、支持用户输入的地铁线路的查询
3、提供用户指定两站点间的最短乘车路径的方案(可换乘)
五、数据存储格式
1、subway.txt存储的地图数据文本,使用json格式:
[ { 'Line' : 'S1线', 'Station' : ['金安桥', '四道桥', '桥户营', '上岸', '栗园庄', '小园', '石厂'] }, ……………… ……………… ……………… ]
'Line'为线路名称,'Station' 为站名。
2、数据的读取和保存
public static String readJsonData(String pactFile){ //读入json格式的地铁路线信息 StringBuffer strbuffer = new StringBuffer(); File myFile = new File(pactFile); if (!myFile.exists()) { System.err.println("没有找到文件: " + pactFile); } try { FileInputStream fis = new FileInputStream(pactFile); InputStreamReader inputStreamReader = new InputStreamReader(fis, "GBK"); BufferedReader in = new BufferedReader(inputStreamReader); String str; while ((str = in.readLine()) != null) { strbuffer.append(str); } in.close(); } catch (IOException e) { e.getStackTrace(); } return strbuffer.toString(); } public static String getPath(String fileName) { //获取文件路径 String path = Subway.class.getResource("").toString() + fileName; path = path.replace("file:/", ""); path = path.replace("/", "//"); return path; } public static boolean save(String path, String sets) throws IOException { //保存文件 FileWriter fw; File f=new File(path); if (!f.exists()) { f.createNewFile(); } FileOutputStream fos1=new FileOutputStream(f); OutputStreamWriter dos1=new OutputStreamWriter(fos1); dos1.write(sets); dos1.write("\n"); dos1.close(); return true; }
运行截图:
主界面:
读取成功提示:
读取成功后显示地铁线路所有信息:
UI自己写的,太丑勿喷
六、具体功能实现
因为地铁线是无向图,若用邻接矩阵存会是稀疏矩阵对空间浪费较大,故采用邻接表。
1、邻接表定义:
public class ENode {//定义边节点 int ivex; // 该边所指向的顶点的位置 ENode nextEdge; // 指向下一条弧的指针 } public class VNode { //定义顶点节点 final static int inf = Integer.MAX_VALUE; //代表距离无限大 String data; // 顶点信息(站点名字) ENode firstEdge; // 指向该顶点所连接的边 int dist; // 距离 boolean flag; // 标记(是否被访问过,dijkstra算法中用到) VNode parent; // 父节点(即前一站) public VNode() { //无参构造器 this.dist = inf; //初始化距离为无限大 this.flag = false; //初始化节点未访问 this.parent=null; //初始化父结点为空 } } private static VNode[] mVexs; // 顶点数组(构造邻接表)
这里其实写麻烦了,跟当初写C语言一样自己定义,弄得后面功能实现麻烦了很多,如果用map之类的写会简单很多。
2、bfs计算最短路径:
private static void bfs(VNode v1, String s){ //更新路径信息 Listqueue = new LinkedList (); queue.add(v1); while(true) { int count=queue.size(); for(int i=0;i ) { VNode v = queue.get(0); if(v != null) { ENode node = v.firstEdge; while (node != null) { VNode childVNode = mVexs[node.ivex]; if(childVNode.flag==false) { childVNode.flag=true; //将标志置为true,表示该节点已被访问 childVNode.dist=v.dist+1; //距离+1 childVNode.parent=v; //将该节点设为父结点(为了便于回溯) if(childVNode.data.equals(s)) return; queue.add(childVNode); } node = node.nextEdge; } } queue.remove(0); } } }
按自己的理解写的,没参考网上代码,可能比较垃圾,不要见怪
就本题而言,bfs实现简单,而且当我们找到终点时可以直接终止循环。
3、………………
其他代码太长就不贴了,具体可去我的GitHub上看,链接已在文章开头给出
顺便附上部分运行截图:
查看某指定路线:
查询两站点间最短路径:
该最短路径可以保存到本地txt文件:
因为获取了开始读取的subway.txt中的线路站点信息,并用下拉框的形式进行选择,确保了不会有非法输入,省去了异常判断的处理。
附录:
GitHub下载请自行把bin和src目录下的代码丢进相应文件夹下,如果报红,原因是json的jar包还没有导入:
发现我的数据文件忘记上传了,尴尬,请把subway.txt放在bin目录下。
现已重新上传subway.txt至bin目录下,直接运行就好。