1. 程式人生 > >使用優先佇列優化後的Dijkstra演算法

使用優先佇列優化後的Dijkstra演算法


一個簡單的模板,需要注意的是當權值不存在或者權值都相同時,dijkstra演算法變成bfs,而且dijkstra演算法處理不了負權邊情況。

一個重要的區別:(dijkstra與prim的區別)

在dij演算法中dis[i]陣列表達的意義是:節點i到源點start最短距離,鬆弛操作dis[i]>dis[t]+map[t][i](t節點是最新確定的最短路徑節點,i節點既是t的一個鄰接節點)的意義是:t的鄰接節點i到源點start的最小距離(dis[i]),是否大於 最新節點t到原點的最短距離(dis[t])+節點t與節點i的權值(map[t][i]),這一過程都在圍繞dis[i]陣列進行。

在prim演算法中dis[i]陣列表達的意義是:節點i到  任意mst'節點' 的最小距離,dis[i]>x[t][i],(i為任意的未訪問過的節點,t為最新的mst節點)意思是如果i節點到mst節點的距離要  大於  最新的mst節點t和i節點的權值,那麼很顯然為了滿足總權值最小一定會有dis[i]=x[t][i]。

輸出個點到原點的最短距離

第一行的數是源點,第二行的數是節點個數,第三行的數是邊數

接下來的分別是初始節點,結束節點,和權值


測試用例:

1
5
7
1 2 2
1 3 3
2 3 5
2 4 6
3 4 7
3 5 1

4 5 4

答案:

0

2

3

8

4


import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;



public class 單源點最短路_Dijkstra演算法 {

	/**
	 * 應用優先佇列優化dijkastra解決單源點最短路徑問題
	 * @param args
	 */
	static class node{
		public int nod;//節點標號
		public int sum;//當前nod節點到源點的最小的距離
		node(int nod,int sum){
			this.nod=nod;
			this.sum=sum;
		}
	}
	//鄰接表節點
	static class edge{
		public int a;//起始節點
		public int b;//結束節點
		public int w;
		edge(int a,int b,int w){
			this.a=a;
			this.b=b;
			this.w=w;
		}
	}
	static final int INF=Integer.MAX_VALUE;
	static int vis[];//標記陣列
	static int dis[];//dis[i]的意義是節點i到 源點 的最短路徑,整個過程就是在維護這個陣列
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int start=sc.nextInt();//起始點位置
		int n=sc.nextInt();//圖的節點個數
		int m=sc.nextInt();//邊數
//		sc.nextLine();
//		int map[][]=new int[n+1][n+1];//鄰接矩陣
		//初始化鄰接矩陣都為INF
//		for (int i = 0; i < map.length; i++) {
//			for (int j = 0; j < map[0].length; j++) {
//				map[i][j]=INF;
//			}
//		}
		//鄰接表
		ArrayList<edge>[] map=new ArrayList[n+1];
		for (int i = 0; i < map.length; i++) {
			map[i]=new ArrayList<edge>();
		}
		//將資料錄入鄰接矩陣
		for (int i = 0; i < m; i++) {
			int a=sc.nextInt();
			int b=sc.nextInt();
			int w=sc.nextInt();
			map[a].add(new edge(a,b,w));
			//鄰接矩陣方法
//			map[a][b]=Math.min(map[a][b],w);//可能會有兩個節點間存在兩條路徑,總是選取最小的那條
		}
		//執行演算法
		dijkstra(start,map,n,m);
		
		for (int i = 1; i < dis.length; i++) {
			System.out.println(dis[i]);
		}
	}
	/**
	 * 
	 * @param start 初始節點
	 * @param map 鄰接矩陣
	 * @param n 節點數量
	 * @param m 邊的數量
	 */
	private static void dijkstra(int start, ArrayList<edge>[] map, int n, int m) {
		//重寫comparator方法,規定佇列中小的數優先出隊
		Comparator<node> com=new Comparator<node>(){
			@Override
			public int compare(node o1, node o2) {
				int num1=o1.sum;
				int num2=o2.sum;
				if(num1>num2){
					return 1;
				}else if (num1<num2) {
					return -1;
				}else {
					return 0;
				}
			}
		};
		//定義優先佇列,初始值為11,比較器為com
		Queue<node> q=new PriorityQueue<node>(11,com);//初始化優先佇列
		q.offer(new node(start,0));//初始節點入隊
		vis=new int[n+1];//初始化 標記表
		dis=new int[n+1];//初始化 各節點到源點的最小距離表
		//都初始化為INF
		for (int i = 0; i <= n; i++) {
			dis[i]=INF;
		}
		//start節點到自身的距離為0
		dis[start]=0;
		while (q.size()!=0) {//當優先佇列不為空時
			node temp=q.poll();//彈出並刪除佇列頭結點
			int t=temp.nod;//提取頭節點下表數
			if(vis[t]==1){//如果該節點已經被訪問過了,直接跳過這次迴圈
				continue;
			}
			vis[t]=1;//沒有訪問過,標記訪問過
			//遍歷所有t的鄰接節點
			for (int i = 0; i < map[t].size(); i++) {
				//如果該節點沒有被訪問過,且,tempnode到能夠到達該節點(鄰接)
				if(vis[map[t].get(i).b]==0 && map[t].get(i).w<INF){
					//鬆弛操作,更新dis表,如果這個鄰接節點map[t].get(i).b到原地的距離要  大於  當前節點t到原點的距離+t節點到他的鄰接點map[t].get(i).b 的距離之和
					if(dis[map[t].get(i).b]>dis[t]+map[t].get(i).w){
						dis[map[t].get(i).b]=dis[t]+map[t].get(i).w;//更新鄰接點map[t].get(i).b到遠點的距離
						q.offer(new node(map[t].get(i).b,dis[map[t].get(i).b]));//將更新的節點壓入優先佇列
					}
				}
			}
		}
	}
}