迪傑斯特拉(dijkstra)-兩個地鐵站最短距離

前言

最新更新了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

算法原理

算法原理我相信搜索能搜出一大把,我提出較爲重要的點並輔以代碼展示

  1. 尋找分析點的所有相鄰點並記錄權值
  2. 選取最短距離的鄰點B
  3. 循環遍歷所有

類職責劃分

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);
    }
}

算法

對應上面算法最關鍵的三點

  1. 找所有相鄰點
  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;
    }
  1. 計算最小權值並找出下一個需要分析的點
//通過計算最小權值 計算下一個需要分析的點
    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;
    }
   
  1. 循環遍歷後獲取結果

    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和大數據問題,轉載請註明作者和原文鏈接,感謝
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章