1. 程式人生 > >PriorityQueue+Dijkstra優先佇列優化的Dijkstra

PriorityQueue+Dijkstra優先佇列優化的Dijkstra

前面分別介紹了“原生的Dijkstra”即毫無優化的Dijkstra,但這種Dijkstra的效率較低為n^n,因此面對較大資料量的時候需要對其進行優化,也就是優化所採用的貪心策略的實現,因此就有了Heao+Dijkstra堆優化的Dijkstra,但是堆優化的實現很複雜,而PriorityQueue+Dijkstra優先佇列優化的Dijstra的效率雖然略低於堆優化的Dijkstra,但是實現卻容易的多,也不容易出錯,因為可以藉助java類庫中的PriorityQueue來實現,因此優先佇列優化的Dijkstra是首選,其實java類庫PriorityQueue的底層實現原理就是推排序微笑

還以藍橋杯“最短路”為例實現PriorityQueue+Dijkstra:

 演算法訓練 最短路   時間限制:1.0s   記憶體限制:256.0MB 問題描述

給定一個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。

輸入格式

第一行兩個整數n, m。

接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。

輸出格式 共n-1行,第i行表示1號點到i+1號點的最短路。 樣例輸入 3 3
1 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提升還是非常明顯的: