1.題目

分析與解題思路
dijkstra算法是典型的用來解決單源最短路徑的算法,該算法采用貪心的思想,廣度優先搜索的策略,每一輪從當前節點找對與其鄰接的所有節點進行放松操作(比較距離源點的距離,來決定是否執行),記錄當前節點為已訪問,之后從所有未訪問過的節點中找到距離源點最近的節點作為當前節點,重復上述操作。BFS策略體現在每次從當前節點,訪問所有與其鄰接的節點;貪心的思想體現在,每一輪之后,當前距離最短的點S被認為不存在從源點到S更短的路徑,每次當通過S對與其鄰接的節點進行放松后,可以將S從集合剔除,因為通過后續的到達S節點的距離一定更長。
證明:
假設源點為S0,當前節點為S,節點S0到節點u的距離為dis[u];
因為當前節點為S,說明其他節點u!=S,必有dis[u]>dis[S]
假如后續的節點v有一條到S的路徑,源點到v的距離必然是通過S或者u進行放松的
dis[v]>=min{dis[u],dis[S]}
另外dijkstra算法只能解決權值為正的情況,當存在負權值時,算法可能出錯,比如:
節點從1開始,根據貪心思想,下一下節點應該選擇3,記錄dis[3]=2
但是有更短的路徑1->2->3 距離為1
測試用例

(測試用例1)

(測試用例2)
輸入說明:
第一行:測試用例的數目
對於每一個測試用例,第一行,n,m,s;n代碼節點的個數,m代表邊的條數,s代碼源節點
后面m行,u,v,w代表一條u->v權值為w的邊
程序實現
1 public List<int[]>dijkstra1(int s);使用的是課本上比較朴素的實現方法。
下面是我自己的實現方法:
//采用優先隊列優化
1 public List<int[]>dijkstra2(int s){ 2 3 int[]path=new int[n+1]; 4 5 int[]dis=new int[n+1]; 6 7 boolean[]mark=new boolean[n+1]; 8 9 Arrays.fill(dis, INF); 10 11 dis[s]=0; 12 13 path[s]=0; 14 15 PriorityQueue pq=new PriorityQueue(n); 16 17 for(int i=1;i<n;++i) { 18 19 for(Node temp:table.get(s)) { 20 21 if(!mark[temp.index]&&dis[s]+temp.weight<dis[temp.index]) { 22 23 if(dis[temp.index]==INF) { 24 25 dis[temp.index]=dis[s]+temp.weight; 26 27 pq.offer(temp.index, dis[temp.index]); 28 29 }else { 30 31 pq.increase(temp.index, dis[s]+temp.weight-dis[temp.index]); 32 33 dis[temp.index]=dis[s]+temp.weight; 34 35 } 36 37 path[temp.index]=s; 38 39 } 40 41 } 42 43 mark[s]=true; 44 45 s=pq.poll(); 46 47 } 48 49 ArrayList<int[]>res=new ArrayList<int[]>(2); 50 51 res.add(path); 52 53 res.add(dis); 54 55 return res; 56 57 }
使用優先隊列優化,每次尋找最小的距離的節點的時候就不用線性遍歷整個表,可以在logV的時間復雜度內獲得最小距離的節點;同時我在使用優先隊列的時候,我的實現方式對優先隊列有以下額外要求:(優先隊列的代碼有點長,具體情況源代碼)
1) 能夠更新元素
先找到指定的元素,根據2)中的方法,然后更新dis,如果dis變小進行上濾,dis變大進行下濾。
2) 能夠在常數時間內找到在圖中某個標號的節點在優先隊列中的位置
一個很常見的思路就是使用空間換時間,使用一個數組保存Node在優先隊列中的位置,並且在優先隊列進行插入,刪除,更新時也要同步更新。
運行截圖

結論
朴素的實現時間復雜度是O(E*V)(我使用的是鄰接表存儲圖),使用優先隊列優化時間復雜度為O(VlogV);關於圖的存儲,稀疏圖使用鄰接表占用內存比較少,稠密圖適合用鄰接矩陣,訪問速度很快。
