狄克斯特拉算法--Java實現

狄克斯特拉算法(Dijkstra’s algorithm)

爲了什麼而存在

這個算法主要用在圖這種數據結果中,而且是比較特殊的 加權圖 。這裏引用《算法圖解》中的一個圖片來進行講解。
在這裏插入圖片描述
比如我們要從雙子峯到金門大橋,我們想在最短的時間內到達。這裏就不是段數最少的問題了,因爲條道路都有耗時的時間,可以看成每條路的權重,你要找的就是最後權重累加最少的路徑。
上面屬於加權圖,計算這種加權圖的最短路徑的方法就是用 狄克斯特拉算法
如果不是加權圖,那麼用的就是廣度優先搜索來解決最短路徑問題。以下的圖片就是非加權圖:
在這裏插入圖片描述
在搞清楚這個算法之前,我們先要知道**“圖”** 這種數據結構。
圖這種數據結構,可以說有着非常大的派系了,比如下面這中圖屬於無向圖:
在這裏插入圖片描述
下面這種帶着方向的,我們叫它有向圖
在這裏插入圖片描述
如果在每條邊加上權重,我們就叫它 加權有向圖 ,這個屬於,
在這裏插入圖片描述
如果無向的,我們一般叫做帶權圖
在這裏插入圖片描述

如何實現

(1) 找出最便宜的節點,即可在最短時間內前往的節點。
(2) 對於該節點的鄰居,檢查是否有前往它們的更短路徑,如果有,就更新其開銷。
(3) 重複這個過程,直到對圖中的每個節點都這樣做了。
在這裏插入圖片描述
(1)首先,我們需要確定初始點在哪裏
(2)根據初始點,我們先把和初始點接近的點的開銷確定了
(3)然後取出開銷最小的那個點,然後開始得到開銷最小的點的本身的開銷和它到達它的鄰居的開銷。
(4)然後更新他的鄰居的開銷。
(5)接着再尋找出還未處理點中再尋找最小的開銷的那個點繼續執行(2)的步驟。
Show The code

public int getShortestPath( Map<String, HashMap<String, Integer>>  graph, String start, String end) {
        HashSet<String> processedNodeSet = new HashSet<>();
        HashMap<String, Integer> costsMap = new HashMap<>();
        HashMap<String, String> parentsMap = new HashMap<>();
        for (String node : graph.keySet()) {
            costsMap.put(node, Integer.MAX_VALUE);
        }
        HashMap<String, Integer> startNeighbors = graph.get(start);
        for (String item : startNeighbors.keySet()) {
            costsMap.put(item, startNeighbors.get(item));
        }

        String node = findLowestCostNode(costsMap, processedNodeSet);
        while (node != null) {
            Integer cost = costsMap.get(node);
            HashMap<String, Integer> neighbors = graph.get(node);
            for (String key : neighbors.keySet()) {
                int newCost = cost + neighbors.get(key);
                if (newCost < costsMap.get(key)) {
                    costsMap.put(key, newCost);
                    parentsMap.put(key, node);
                }
            }
            processedNodeSet.add(node);
            node = findLowestCostNode(costsMap, processedNodeSet);
        }
        return costsMap.get(end);
    }

    public String findLowestCostNode(Map<String, Integer> costs, HashSet<String> processedNodeSet) {
        String ans = null;
        int lowCost = Integer.MAX_VALUE;

        for (String key : costs.keySet()) {
            if (costs.get(key) < lowCost && !processedNodeSet.contains(key)) {
                ans = key;
                lowCost = costs.get(key);
            }
        }
        return ans;
    }

時間複雜度:O(n2)O(n^2)
時間複雜度:O(n2)O(n^2)

幾個關鍵疑問

爲什麼該算法不能用在負的帶權的邊的背景之下?

因爲我們都是從當前的條件中選擇開銷最小的點來處理,如果有負的開銷的點,那麼就意味着,我們不能夠每次都選取當前的最小節點來進行更新了,因爲當某個邊右通向負的帶權的點的時候,就會產生新的最小開銷的點,而那個點極有可能是我們已經處理過的。所以就導致就算更新了這個點,我們也依然沒法使用。

該算法是否有優化的空間?

我們從算法中可以知道,我們每次其實都在不斷的重複尋找未處理的最小開銷的點的這個步驟,尋找最小點,這個思想就像我們之前的學到的最小堆的使用思想一致,每次只要花O(logN)O(logN) 就能在每次取出最小點。以後有空可以實現一下子。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章