無論是算法分析課程還是Java課程設計,都對有向圖中的最短路徑情有獨鍾,今天準備對單願最短路徑的解決方案進行一下詳解,免得每一次用到都是從頭再來2333
適用條件:
在有向圖中,求一個頂點到其他頂點的最短路徑
首先讓我們先回顧一下,最簡單的多源最短路徑的求法:Floyd算法
實例講解
這裏呢我們通過這個例題對這個算法進行解析:(節選自《啊哈算法》)
題目中的關係如圖所示:
這裏呢,我們依舊使用二維數組來存儲頂點之間邊的關係
爲了讓最後單源路徑最短的問題方便讀取答案,創建一個1號頂點到其他頂點初始路程的數組dist
求解思路
1、首先從距離起點最近的點進行操作,因爲起點1不可能通過其他頂點獲得到達該最近點的最短路徑。
2、那麼我們找到了頂點2,和Floyd方法類似,這個時候我們開始經過頂點2對頂點1的其他邊進行優化。
3、比較dist[3]與dist[2]+e[2][3]的大小關係,如果dist[3]較大,則進行更新,這就使得頂點1到3的最短路徑變短。
4、接下來,再剩下的3,4,5和6號頂點中,繼續尋找離1號起點最近的頂點,進行路徑更新。
5、最終通過其他頂點的優化後的道德單源最短路徑爲:
總結算法:
1、通過book[i]是否爲1判斷點i是否已經是最短路徑的頂點集合P中,否則在Q中。
2、在Q中選取一個離起點最近的頂點加入到集合P中,並考察所有的以點u爲起點的邊
3、重複第二步,直到Q爲空
算法實現:
/*
*C語言實現,最簡
*/
#include <stdio.h>
int main()
{
int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
int inf = 99999999;//用inf(infinity的縮寫)存儲一個我們認爲的正無窮值
//讀入n和m,n表示頂點個數,m表示邊的條數
scanf("%d %d",&n,&m);
//初始化
for(i = 1;i <= n;i++) {
for(j = 1;j <= n;j++){
if(i == j) e[i][j] = 0;
else e[i][j] = inf;
}
}
//讀入邊
for (int i = 0; i < m; i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2] = t3;
}
//初始化dis數組
for(i = 1;i <= n;i++) {
dis[i] = e[1][i];
}
//book數組初始化
for(i = 1;i <= n;i++)
book[i] = 0;
book[1] = 1;
//Dijkstra算法核心語句
for(i = 1;i <= n - 1;i++)
{
//找到離1號頂點最近的頂點
min = inf;
for(j = 1;j <= n;i++)
{
if(book[j] == 0 && dis[j] < min)
{
min = dis[j];
u = j;
}
}
book[u] = 1;
for(v = 1;v <= n;v++)
{
if(e[u][v] < inf){
if(dis[v] > dis[u] + e[u][v])
dis[v] = dis[u] + e[u][v];
}
}
}
//輸出最終結果
for(i = 1;i <= n;i++)
prntf("%d ",dis[i]);
getchar();
getchar();
return 0;
}
/*
*Java實現,非最優化解
*/
package com.stortest.path;
import java.util.ArrayList;
import java.util.Scanner;
public class Dijkstra {
public long[] result;//定義全局變量存儲到各點的最短路徑
public class Edge{
public int a,b,c;
Edge(int a,int b,int c){//聲明構造函數
this.a = a;
this.b = b;
this.c = c;
}
}
public boolean get(int n, int s, Edge[] A) {
ArrayList<Integer> list = new ArrayList<Integer>();//生成list列表
//初始化
result = new long[n];
boolean[] used = new boolean[n];//存儲訪問狀態
int[] num = new int[n];
for(int i = 0;i < n;i++) {
result[i] = Integer.MAX_VALUE;
used[i] = false;
}
result[s] = 0; //第s個頂點到自身距離爲0
used[s] = true; //表示第s個頂點進入數組隊
list.add(s); //第s個頂點入隊
while(list.size() != 0) {
int a = list.get(0); //獲取數組隊中第一個元素
list.remove(0); //刪除數組隊中第一個元素
for(int i = 0;i < A.length;i++) {
//當list數組隊的第一個元素等於邊A[i]的起點時
if(a == A[i].a && result[A[i].b] > result[A[i].a] + A[i].c) {
//判斷 起點到終點的最短路徑 是否可以更新
result[A[i].b] = result[A[i].a] + A[i].c;
if(!used[A[i].b]) {//如果終點是第一次被遍歷
list.add(A[i].b);
used[A[i].b] = true; //表示邊A[i]的終點b已進入數組隊
}
}
}
// used[a] = false; //頂點a出數組對
}
return true;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Dijkstra text = new Dijkstra();//創建對象
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int m = input.nextInt();
Edge[] A = new Edge[m];//創建內部類對象數組,存儲邊的關係
for(int i = 0;i < m;i++)
{
int a = input.nextInt() - 1;
int b = input.nextInt() - 1;
int c = input.nextInt();
A[i] = text.new Edge(a,b,c);
}//接收輸入數據
if(text.get(n,0,A)){//調用子函數,輸出到最遠點的結果
System.out.println(text.result[text.result.length - 1]);
}
}
}
改進:
該算法的時間複雜度爲O(N2),對於邊數M少於N2的稀疏圖來說,可以使用鄰接矩陣進行優化,可以使時間複雜度優化到(M+N)logN