项目介绍:
提供一副地铁线路图,计算指定两站之间最短(最少经过站数)乘车路线;输出指定地铁线路的所有站点。以北京地铁为例,地铁线路信息保存在data.txt中,格式如下:
地铁线路总数
线路名1 站名1 站名2 站名3 ...
线路名2 站名1 站名2 站名3 ...
线路名3 站名1 站名2 站名3 ......
需求分析:
1、主要功能
(1)能实现地铁信息的导入
(2)能够保存各个站点的信息
(3)输入起始站点和目标站点后计算出指定两站之间最短乘车路线
(4)能输出乘车线路经过的所有站点的信息
(5)能够给出站点的换乘信息
(6)若有多个最短路线时优先选择换乘少的线路
(7)若有多个最优解则全部输出
2、实现语言
java
3、实现算法
Dijkstra算法
①基本思想:设置一个集合S存放已经找到最短路径的顶点,S的初始状态只包含源点v,对vi∈V-S,假设从源点v到vi的有向边为最短路径。以后每求得一条最短路径v, …, vk,就将vk加入集合S中,并将路径v, …, vk , vi与原来的假设相比较,取路径长度较小者为最短路径。重复上述过程,直到集合V中全部顶点加入到集合S中。
4、类职责划分(相关类的功能进行描述)
- Line类
用于保存线路信息
List<Station> line | 保存线路信息 |
- Station类
用于保存站点信息,线路名称,测试站点到某一个目标站点所经过的所有站集合
String name | 站点名称 |
String Linename | 线路名称 |
Station prevsation | 在当前线路时,所在站点的前一个站 |
Station nextsation | 在当前线路时,所在站点的后一个站 |
Map<Station,LinkedHashSet<Station>> ordermap | 测试站点到某一个目标站点(key)所经过的所有站集合 |
- ReadFile类
基本功能时读取文件“地铁线路信息.txt”内容,储存到Line,Station中,保存所有线路及该线路的站点信息,站点总数
Set<List<Station>> lineSet | 保存所有线路信息 |
totalStation | 保存站点总数 |
readText() | 函数用于读取文件 |
- dijkstra类
用于计算最短路径得出结果输出
List<Station> getAllNearStations(Station station) | 函数用于得到与传入参数所有相邻的站点 |
Station getShortPath(Station station) | 函数用于得到参数到各个站的最短距离站 |
void result(Station s1,Station s2) | 函数用于得出最终结果并输出 |
void main(String[] args) | 主函数用于输入起始站点 |
5、核心代码
Line类
public class Line { //集合保存线路信息 public static List<Station> line = new ArrayList<Station>(); public static List<Station> getLine() { return line; } public static void setLine(List<Station> line) { Line.line = line; } }
Station类
public class Station { //地铁站名称 private String name; //地铁线路名 private String Linename; //在当前线路时,所在站点的前一个站 public Station prevsation; //在当前线路时,所在站点的后一个站 public Station nextsation; //测试站点到某一个目标站点(key)所经过的所有站集合 private Map<Station,LinkedHashSet<Station>> ordermap = new HashMap<Station,LinkedHashSet<Station>>(); public String getLinename() { return Linename; } public void setLinename(String linename) { Linename = linename; } public Station (String name){ this.name = name; } public Station() { // TODO Auto-generated constructor stub } public String getName() { return name; } public void setName(String name) { this.name = name; } //记录当前测试站点到目标站点所经过的所有站点,并储存 public LinkedHashSet<Station> getAllPassedStations(Station station) { if(ordermap.get(station) == null){ LinkedHashSet<Station> set = new LinkedHashSet<Station>(); set.add(this);//this为当前类的实例 ordermap.put(station, set); } return ordermap.get(station); } public Map<Station, LinkedHashSet<Station>> getOrderMap() { return ordermap; } @Override public boolean equals(Object obj) { if(this == obj){ return true; } else if(obj instanceof Station){ Station s = (Station) obj; if(s.getName().equals(this.getName())){ return true; } else { return false; } } else { return false; } } @Override public int hashCode() { return this.getName().hashCode(); } }
ReadFile类
public class ReadFile { //集合保存所有线路及该线路的站点信息 public static Set<List<Station>> lineSet = new HashSet<List<Station>>(); //所有站点数 public static int totalStation = 0; //文件读取函数 public static void readText(){ Station Prevstation = new Station(); try { File file=new File("地铁线路信息.txt"); //判断文件是否存在 if(file.isFile() && file.exists()){ InputStreamReader reader = new InputStreamReader(new FileInputStream(file)); BufferedReader bufferedReader = new BufferedReader(reader); String stationline = null; int flag = 0; while((stationline = bufferedReader.readLine()) != null){ String[] tmp = stationline.split(" ");//分割符号,将空格置空 List<Station> Linestations = new ArrayList<Station>();//设置一个链表保存线路信息 for(int j=1;j<tmp.length;j++) { Station station = new Station(); station.setLinename(tmp[0]);//将每一行的第一个字符串存入线路名 station.setName(tmp[j]);//保存站点名称 Linestations.add(station);//保存线路信息 totalStation++;//计算总站点数 if(flag != 0) {//当flag不为零时,存在前后站点 station.prevsation = Prevstation; Prevstation.nextsation = station; } Prevstation = station;//记录从零开始记录的站点 flag++; } lineSet.add(Linestations);//添加为线路集 } // 关闭文件 reader.close(); bufferedReader.close(); } else System.out.println("找不到指定的文件!"); } catch (Exception e) { System.out.println("文件内容出错!"); e.printStackTrace(); } } }
dijkstra类
public class dijkstra { private List<Station> List1 = new ArrayList<Station>();//保存已经遍历过的站点 //得到与传入参数所有相邻的站点 private List<Station> getAllNearStations(Station station){ List<Station> linkedStaions = new ArrayList<Station>(); for(List<Station> line : ReadFile.lineSet){//遍历所有线路 if(line.contains(station)){//当前线路中包含参数站点 Station s = line.get(line.indexOf(station));//取出当前线路上的该站点的值 if(s.prevsation != null){ linkedStaions.add(s.prevsation);//得到当前线路的该站点的前一个站点 } if(s.nextsation != null){ linkedStaions.add(s.nextsation);//得到当前线路的该站点的后一个站点 } } } return linkedStaions; } //得到参数到各个站的最短距离每一站距离为1 private Station getShortPath(Station station){ int min = 10000;//定义一个较大的数用做比较 Station rets = null; for(Station s :station.getOrderMap().keySet()){//遍历当前测试站点到所有站点 if(List1.contains(s)){ continue; } LinkedHashSet<Station> set = station.getAllPassedStations(s);//当前测试站点到未遍历站点的路径 if(set.size() < min){ min = set.size();//取路径最短的哪一个 rets = s; } } return rets;//返回最近的这一个站点 } //计算最短经过路径 public void result(Station s1,Station s2){ int flag = 0; String tempname = ""; String linename = ""; int flag1=0; //遍历所有站点,判断输入的站名名称是否存在 for(List<Station> line : ReadFile.lineSet){ if(line.contains(s1)){ flag1=1; } } if(flag1==0) { System.out.println("输入站点不存在,请重新输入!"); return ; } int flag2=0; for(List<Station> line : ReadFile.lineSet){ if(line.contains(s2)){ flag2=1; } } if(flag2==0) { System.out.println("输入站点不存在,请重新输入!"); return ; } //当所有站点都遍历完 if(List1.size() == ReadFile.totalStation){ System.out.println("起点:"+s1.getName()+" 终点站:"+s2.getName()+",共经过"+(s1.getAllPassedStations(s2).size()-1)+"站"); int p=0; for(Station station : s1.getAllPassedStations(s2)){//输出遍历s1到s2经过的路径 p++; if(flag == 0) { tempname = station.getName(); } else if(flag == 1) { linename=station.getLinename(); System.out.print(station.getLinename()+tempname+"->"); } else { if(!station.getLinename().equals(linename)) {//换乘线路换行输出 linename=station.getLinename(); System.out.println(); } if(p<s1.getAllPassedStations(s2).size()-1) { System.out.print(station.getLinename()+" "+station.getName()+"->"); } else { System.out.print(station.getLinename()+" "+station.getName()); } } flag++; } return; } if(!List1.contains(s1)){//将当前测试站点储存在已遍历链表 List1.add(s1); } //如果起点的测试站点的OrderMap为空,则第一次用该站的相邻站点初始化 if(s1.getOrderMap().isEmpty()){ List<Station> Linkedstations = getAllNearStations(s1); for(Station s : Linkedstations){ s1.getAllPassedStations(s).add(s); } } Station psation = getShortPath(s1);//获取距离起点站s1最近的一个站 if(psation == s2){ System.out.println("找到目标站点:"+s2+",共经过"+(s1.getAllPassedStations(s2).size()-1)+"站"); for(Station station : s1.getAllPassedStations(s2)){ System.out.print(station.getName()+"->"); } return; } for(Station station1 : getAllNearStations(psation)){ if(List1.contains(station1)){ continue; } int shortestPath = (s1.getAllPassedStations(psation).size()-1) + 1;//计算所需距离 if(s1.getAllPassedStations(station1).contains(station1)){ //如果s1已经计算过到此station1的经过距离,那么比较出最小的距离 if((s1.getAllPassedStations(station1).size()-1) > shortestPath){ //重置S1到周围各站的最小路径 s1.getAllPassedStations(station1).clear(); s1.getAllPassedStations(station1).addAll(s1.getAllPassedStations(psation)); s1.getAllPassedStations(station1).add(station1); } } else { //如果s1还没有计算过到此station1的经过距离 s1.getAllPassedStations(station1).addAll(s1.getAllPassedStations(psation)); s1.getAllPassedStations(station1).add(station1); } } List1.add(psation); result(s1,s2);//重复计算,往外面站点扩展 } public static void main(String[] args) { dijkstra subway = new dijkstra(); ReadFile.readText(); Scanner input =new Scanner(System.in); System.out.print("请输入起点:"); String str1 = input.next(); System.out.print("请输入终点:"); String str2 = input.next(); subway.result(new Station(str1), new Station(str2)); } }
6、测试用例
测试一:站点输入有误
测试二:不用换乘
测试三:不用换乘
测试四:换乘
测试五:换乘
7、总结
1.没有完成需求分析中的选择换乘少的路线和输出全部最优解,要进一步完善思考。
2.写代码的过程中,意识到自己对于最短路径算法的理解不透彻,完成过程中查找了很多资料。
3.认识到自己的不足要多多锻炼努力。
完整代码已上传https://github.com/tongyao063/SubWay2.git