前言
最新更新了github。歡迎多評論+討論,共同努力。
往後準備更新大數據和微服務的BLOG
由於項目需要計算兩個地鐵站之前最短距離及其線路流程。
引發使用迪傑斯特拉算法計算帶權值兩點之前最短距離。
網上資料多用的是C++寫的算法,在這裏用的是Java。實現的方法可以有很多,重要的是把原理理解後用清晰的代碼實現。
這裏參考了網上的資料:https://blog.csdn.net/wangchsh2008/article/details/46288967
發現類的職責不十分明確並且邏輯稍微混亂進行了改進。並且加入真實距離後會產生BUG
Github
地址:https://github.com/ithuhui/hui-core-algorithm-dijkstra
分支:master
位置:com.hui.algorithm.app.DijkstraApp
算法原理
算法原理我相信搜索能搜出一大把,我提出較爲重要的點並輔以代碼展示
- 尋找分析點的所有相鄰點並記錄權值
- 選取最短距離的鄰點B
- 循環遍歷所有
類職責劃分
DataBuilder 數據初始化 (相鄰點之間距離)(實際中根據情況用TXT或者數據庫讀取初始化數據)
Result 運行結果
Station 站點信息
Code
Station&Result&DataBuilder
private static HashMap<station> resultMap = new HashMap<>();//結果集
private static List<station> analysisList = new ArrayList<>();//分析過的站點
/**
* <b><code>Station</code></b>
* <p/>
* Description:
* <p/>
* <b>Creation Time:</b> 2018/7/19 16:40.
*
* @author huweihui
* @since metrodev2 0.1.0
*/
public class Station {
private String name;
private String line;
private List<Station> linkStations = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public List<Station> getLinkStations() {
return linkStations;
}
public void setLinkStations(List<Station> linkStations) {
this.linkStations = linkStations;
}
public Station(String name, String line) {
this.name = name;
this.line = line;
}
public Station(String name) {
this.name = name;
}
public Station (){
}
@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();
}
@Override
public String toString() {
return "Station{" +
"name='" + name + '\'' +
", line='" + line + '\'' +
", linkStations=" + linkStations +
'}';
}
}
/**
* <b><code>Result</code></b>
* <p/>
* Description:
* <p/>
* <b>Creation Time:</b> 2018/7/24 14:56.
*
* @author huweihui
* @since metrodev2 0.1.0
*/
public class Result {
private Station star;
private Station end;
private Double distance = 0.0D;
private List<Station> passStations = new ArrayList<>();
public Station getStar() {
return star;
}
public void setStar(Station star) {
this.star = star;
}
public Station getEnd() {
return end;
}
public void setEnd(Station end) {
this.end = end;
}
public Double getDistance() {
return distance;
}
public void setDistance(Double distance) {
this.distance = distance;
}
public List<Station> getPassStations() {
return passStations;
}
public void setPassStations(List<Station> passStations) {
this.passStations = passStations;
}
public Result(Station star, Station end, Double distance) {
this.star = star;
this.end = end;
this.distance = distance;
}
public Result(){
}
@Override
public String toString() {
return "Result{" +
"star=" + star +
", end=" + end +
", distance=" + distance +
", passStations=" + passStations +
'}';
}
}
/**
* <b><code>com.richstonedt.metro.app.DataBuilder</code></b>
* <p/>
* Description:
* <p/>
* <b>Creation Time:</b> 2018/7/10 12:36.
*
* @author huweihui
* @since metro-dev 0.1.0
*/
public class DataBuilder {
public static List<Station> line1 = new ArrayList<Station>();
public static List<Station> line2 = new ArrayList<Station>();
public static List<Station> line3 = new ArrayList<Station>();
public static List<Station> line3N = new ArrayList<Station>();
public static List<Station> line4 = new ArrayList<Station>();
public static List<Station> line5 = new ArrayList<Station>();
public static List<Station> line6 = new ArrayList<Station>();
public static List<Station> line7 = new ArrayList<Station>();
public static List<Station> line8 = new ArrayList<Station>();
public static List<Station> line9 = new ArrayList<Station>();
public static List<Station> line13 = new ArrayList<Station>();
public static List<Station> line14 = new ArrayList<Station>();
public static List<Station> lineAPM = new ArrayList<Station>();//APM
public static List<Station> lineGF = new ArrayList<Station>();//廣佛線
public static LinkedHashSet<List<Station>> lineSet = new LinkedHashSet<>();//所有線集合
public static int totalStaion = 0;//總的站點數量
private DataBuilder(){
}
public static void init (String lineStr,List<Station> line,String lineName){
}
private static void getLine(String lineStr,List<Station> line,String lineName){
String[] lineArr = lineStr.split(",");
for (String s : lineArr) {
line.add(new Station(s,lineName));
}
}
static {
String line1Str = "西朗,坑口,花地灣,芳村,黃沙,長壽路,陳家祠,西門口,公園前,農講所,烈士陵園,東山口,楊箕,體育西路,體育中心,廣州東站";
String line2Str = "廣州南站,石壁,會江,南浦,洛溪,南洲,東曉南,江泰路,昌崗,江南西,市二宮,海珠廣場,公園前,紀念堂,越秀公園,廣州火車站,三元里,飛翔公園,白雲公園,白雲文化廣場,蕭崗,江夏,黃邊,嘉禾望崗";
String line3Str = "番禺廣場,市橋,漢溪長隆,大石,廈滘,瀝滘,大塘,客村,廣州塔,珠江新城,體育西路,石牌橋,崗頂,華師,五山,天河客運站";
String line3NStr = "體育西路,林和西,廣州東站,燕塘,梅花園,京溪南方醫院,同和,永泰,白雲大道北,嘉禾望崗,龍歸,人和,高增,機場南,機場北";
String line4Str = "南沙客運港,南橫,塘坑,大涌,廣隆,飛沙角,金洲,蕉門,黃閣,黃閣汽車城,慶盛,東湧,低湧,海傍,石碁,新造,大學城南,大學城北,官洲,萬勝圍,車陂南,車陂,黃村";
String line5Str = "滘口,坦尾,中山八,西場,西村,廣州火車站,小北,淘金,區莊,動物園,楊箕,五羊邨,珠江新城,獵德,潭村,員村,科韻路,車陂南,東圃,三溪,魚珠,大沙地,大沙東,文衝";
String line6Str = "潯峯崗,橫沙,沙貝,河沙,坦尾,如意坊,黃沙,文化公園,一德路,海珠廣場,北京路,團一大廣場,團一大廣場,東湖,東山口,區莊,黃花崗,沙河頂,天平架,燕塘,天河客運站,長湴,植物園,龍洞,柯木塱,高塘石,黃陂,金峯,暹崗,蘇元,蘿崗,香雪";
String line7Str = "廣州南站,石壁,謝村,鍾村,漢溪長隆,南村萬博,員崗,板橋,大學城南";
String line8Str = "鳳凰新村,沙園,寶崗大道,昌崗,曉港,中大,鷺江,客村,赤崗,磨碟沙,新港東,琶洲,萬勝圍";
String line9Str = "飛鵝嶺,花都汽車城,廣州北站,花城路,花果山公園,花都廣場,馬鞍山公園,蓮塘,清布,清塘,高增";
String line13Str = "魚珠,裕豐圍,雙崗,南海神廟,夏園,南崗,沙村,白江,新塘,官湖,新沙";
String line14Str = "新和,紅衛,新南,楓下,知識城,何棠下,旺村,湯村,鎮龍北,鎮龍";
String lineAPMStr = "廣州塔,海心沙,大劇院,花城大道,婦兒中心,黃埔大道,天河南,體育中心南,林和西";
String lineGFStr = "新城東,東平,世紀蓮,瀾石,魁奇路,季華園,同濟路,祖廟,普君北路,朝安,桂城,南桂路,礌崗,千燈湖,金融高新區,龍溪,菊樹,西朗,鶴洞,沙湧,沙園,燕崗";
getLine(line1Str,line1,"line1");
getLine(line2Str,line2,"line2");
getLine(line3Str,line3,"line3");
getLine(line3NStr,line3N,"line3N");
getLine(line4Str,line4,"line4");
getLine(line5Str,line5,"line5");
getLine(line6Str,line6,"line6");
getLine(line7Str,line7,"line7");
getLine(line8Str,line8,"line8");
getLine(line9Str,line9,"line9");
getLine(line13Str,line13,"line13");
getLine(line14Str,line14,"line14");
getLine(lineAPMStr,lineAPM,"lineAPM");
getLine(lineGFStr,lineGF,"lineGF");
lineSet.add(line1);
lineSet.add(line2);
lineSet.add(line3);
lineSet.add(line3N);
lineSet.add(line4);
lineSet.add(line5);
lineSet.add(line6);
lineSet.add(line7);
lineSet.add(line8);
lineSet.add(line9);
lineSet.add(line13);
lineSet.add(line14);
lineSet.add(lineAPM);
lineSet.add(lineGF);
totalStaion = line1.size() + line2.size() + line3.size() + line4.size() + line5.size() + line6.size()
+ line7.size()+line8.size()+line9.size()+line13.size()+line14.size()+lineAPM.size()+lineGF.size();
System.out.println("總的站點數量:" + totalStaion);
}
}
算法
對應上面算法最關鍵的三點
- 找所有相鄰點
private static List<Station> getLinkStations(Station station) {
List<Station> linkedStaions = new ArrayList<Station>();
for (List<Station> line : SZDataBuilder.lineSet) {
for (int i = 0; i < line.size() ; i++) {
if (station.equals(line.get(i))) {
if (i == 0) {
linkedStaions.add(line.get(i + 1));
} else if (i == (line.size()-1) ) {
linkedStaions.add(line.get(i - 1));
}else {
linkedStaions.add(line.get(i+1));
linkedStaions.add(line.get(i-1));
}
}
}
}
return linkedStaions;
}
- 計算最小權值並找出下一個需要分析的點
//通過計算最小權值 計算下一個需要分析的點
private static Station getNextStation() {
Double min = Double.MAX_VALUE;
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;
}
-
循環遍歷後獲取結果
public static Result calculate(Station star, Station end) { if (!analysisList.contains(star)) { analysisList.add(star); } if (star.equals(end)){ Result result = new Result(); result.setDistance(0.0D); result.setEnd(star); result.setStar(star); resultMap.put(star, result); return resultMap.get(star); } if (resultMap.isEmpty()) { List<Station> linkStations = getLinkStations(star); for (Station station : linkStations) { Result result = new Result(); result.setStar(star); result.setEnd(station); String key = star.getName() + ":" + station.getName(); Double distance = DistanceBuilder.getDistance(key); result.setDistance(distance); result.getPassStations().add(station); resultMap.put(station, result); } } Station parent = getNextStation(); if (parent==null){ Result result = new Result(); result.setDistance(0.0D); result.setStar(star); result.setEnd(end); return resultMap.put(end, result); } if (parent.equals(end)) { return resultMap.get(parent); } List<Station> childLinkStations = getLinkStations(parent); for (Station child : childLinkStations) { if (analysisList.contains(child)) { continue; } String key = parent.getName() + ":" + child.getName(); Double distance = DistanceBuilder.getDistance(key); if( parent.getName().equals(child.getName())){ distance = 0.0D; } Double parentDistance = resultMap.get(parent).getDistance(); distance = doubleAdd(distance, parentDistance); List<Station> parentPassStations = resultMap.get(parent).getPassStations(); Result childResult = resultMap.get(child); if (childResult!=null){ if (childResult.getDistance() > distance) { childResult.setDistance(distance); childResult.getPassStations().clear(); childResult.getPassStations().addAll(parentPassStations); childResult.getPassStations().add(child); } }else { childResult = new Result(); childResult.setDistance(distance); childResult.setStar(star); childResult.setEnd(child); childResult.getPassStations().addAll(parentPassStations); childResult.getPassStations().add(child); } resultMap.put(child, childResult); } analysisList.add(parent); calculate(star, end); return resultMap.get(end); }
應用場景
最短距離算法~~~ 算最短距離的時候用的。
當時項目是計算地鐵最優乘坐方式。小項目,就不把擁擠和換線時間加入最優因素了哈。
總結
算法很簡單,但是很多時候遇到新東西,大多數是基於巨人的肩膀進行修修改改,所以當時也是看了那位朋友的代碼,他把所有的距離都當做1進行距離計算,實際就是最短步數是一樣的。思想不難,並且把相鄰站點的真實距離算進去後感覺有某些BUG。代碼結構有點混亂,因此進行了一些修改。
作者
作者:HuHui
轉載:歡迎一起討論web和大數據問題,轉載請註明作者和原文鏈接,感謝