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 s;
	// 映射集合
	static dist[] distArray;
	// 优先级队列
	static PriorityQueue pq;
	static int max = 1000000;

	/**
	 * 结点类
	 * 
	 * @author DuXiangYu
	 */
	static class Node {
		// 邻接顶点map
		private HashMap map = null;

		public void addEdge(int end, int edge) {
			if (this.map == null) {
				this.map = new HashMap();
			}
			this.map.put(end, edge);
		}
	}
	
	/**
	 * dist: 保存源结点至每个结点的最短路径
	 * @author DuXiangYu
	 *
	 */
	static class dist implements Comparable {

		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();
		pq = new PriorityQueue(nodeCount);
		distArray = new dist[nodeCount + 1];
		Node tempNode = firstArray[1];
		for (int i = 2; i < nodeCount + 1; i ++) {
			HashMap 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 m = firstArray[index].map;
			if (m == null) {
				continue;
			}
			Set set = m.keySet();
			Iteratorit = 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);
		}
	}

}

性能测试:

还是对于那个10000个结点和100000条边的测试数据来说,优先级队列优化的平均执行时间为320ms,看得出来比堆优化的执行效率略低,但相对原生的Dijkstra提升还是非常明显的:


PriorityQueue+Dijkstra优先队列优化的Dijkstra_第1张图片

你可能感兴趣的:(最短路径各种调戏)