使用優先佇列優化後的Dijkstra演算法
阿新 • • 發佈:2019-01-04
一個簡單的模板,需要注意的是當權值不存在或者權值都相同時,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]));//將更新的節點壓入優先佇列 } } } } } }