PriorityQueue+Dijkstra優先佇列優化的Dijkstra
阿新 • • 發佈:2018-12-31
前面分別介紹了“原生的Dijkstra”即毫無優化的Dijkstra,但這種Dijkstra的效率較低為n^n,因此面對較大資料量的時候需要對其進行優化,也就是優化所採用的貪心策略的實現,因此就有了Heao+Dijkstra堆優化的Dijkstra,但是堆優化的實現很複雜,而PriorityQueue+Dijkstra優先佇列優化的Dijstra的效率雖然略低於堆優化的Dijkstra,但是實現卻容易的多,也不容易出錯,因為可以藉助java類庫中的PriorityQueue來實現,因此優先佇列優化的Dijkstra是首選,其實java類庫PriorityQueue的底層實現原理就是推排序。
還以藍橋杯“最短路”為例實現PriorityQueue+Dijkstra:
給定一個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。
輸入格式第一行兩個整數n, m。
接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。
輸出格式 共n-1行,第i行表示1號點到i+1號點的最短路。 樣例輸入 3 31 2 -1
2 3 -1
3 1 2 樣例輸出 -1
-2 資料規模與約定
對於10%的資料,n = 2,m = 2。
對於30%的資料,n <= 5,m <= 10。
對於100%的資料,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其他所有頂點。
基於java類庫的PriorityQueue的PriorityQueue+Dijkstra實現:
import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.PriorityQueue; import java.util.Scanner; import java.util.Set; /** * PriorityQueue + Dijkstra演算法求單源最短路徑 * 首推此方法 * 雖然優先順序佇列優化比堆優化效能差一點,差距很小。 * 但是優先順序佇列可以直接使用java類庫中的PriorityQueue來實現, * 而堆優化實現非常複雜。 * * @author DuXiangYu * */ public class DijKstra_link_Queue { static int nodeCount; static int edgeCount; // 鄰接表表頭陣列 static Node[] firstArray; // 最短路徑陣列 // static int[] dist; // S集合,代表著已經找到最短路徑的結點 static HashSet<Integer> s; // 對映集合 static dist[] distArray; // 優先順序佇列 static PriorityQueue<dist> pq; static int max = 1000000; /** * 結點類 * * @author DuXiangYu */ static class Node { // 鄰接頂點map private HashMap<Integer, Integer> map = null; public void addEdge(int end, int edge) { if (this.map == null) { this.map = new HashMap<Integer, Integer>(); } this.map.put(end, edge); } } /** * dist: 儲存源結點至每個結點的最短路徑 * @author DuXiangYu * */ static class dist implements Comparable<dist> { int value; int index; public dist(int value, int index) { this.value = value; this.index = index; } @Override public int compareTo(dist o) { if (o.value < this.value) { return 1; } else if (o.value > this.value) { return -1; } else { return 0; } } } public static void main(String[] args) { Scanner sc = new Scanner(System.in); nodeCount = sc.nextInt(); edgeCount = sc.nextInt(); firstArray = new Node[nodeCount + 1]; for (int i = 0; i < nodeCount + 1; i++) { firstArray[i] = new Node(); } for (int i = 0; i < edgeCount; i++) { int begin = sc.nextInt(); int end = sc.nextInt(); int edge = sc.nextInt(); firstArray[begin].addEdge(end, edge); } sc.close(); long begin = System.currentTimeMillis(); djst(); long end = System.currentTimeMillis(); System.out.println(end - begin + "ms"); } /** * PriorityQueue + Dijkstra演算法實現 */ private static void djst() { s = new HashSet<Integer>(); pq = new PriorityQueue<dist>(nodeCount); distArray = new dist[nodeCount + 1]; Node tempNode = firstArray[1]; for (int i = 2; i < nodeCount + 1; i ++) { HashMap<Integer, Integer> tempMap = tempNode.map; if (tempMap.containsKey(i)) { dist d = new dist(tempMap.get(i), i); pq.offer(d); distArray[i] = d; } else { dist d = new dist(max, i); pq.offer(d); distArray[i] = d; } } s.add(1); while (s.size() < nodeCount) { dist d = pq.poll(); int index = d.index; int value = d.value; s.add(index); // 用indx這個點去更新它的鄰接點到開始點的距離 HashMap<Integer, Integer> m = firstArray[index].map; if (m == null) { continue; } Set<Integer> set = m.keySet(); Iterator<Integer>it = set.iterator(); while (it.hasNext()) { int num = it.next(); if (num == 1) { continue; } dist tempDist = distArray[num]; if (m.get(num) + value < tempDist.value) { pq.remove(tempDist); tempDist.value = m.get(num) + value; pq.offer(tempDist); distArray[num] = tempDist; } } } for (int i = 2; i < nodeCount + 1; i ++) { System.out.println(distArray[i].value); } } }</span></span>
效能測試:
還是對於那個10000個結點和100000條邊的測試資料來說,優先順序佇列優化的平均執行時間為320ms,看得出來比堆優化的執行效率略低,但相對原生的Dijkstra提升還是非常明顯的: