1. 程式人生 > >圖論(三) (一)最短路徑算法 2.Dijkstra算法

圖論(三) (一)最短路徑算法 2.Dijkstra算法

set print turn 重復 跳過 int 算法導論 出發 AS

Dijkstra 算法解決的是帶權重的有向圖上單源最短路徑問題,該算法要求所有邊的權重都為非負值。該算法的時間復雜度是O(N2),相比於處理無負權的圖時,比Bellmad-Ford算法效率更高。

算法描述:

首先引用《算法導論》中的一段比較官方的話,如果可以看懂,那下一部分就可以跳過了:

“Dijkstra算法在運行過程中維持的關鍵信息是一組結點集合S。從源結點s到該集合中每個結點之間的最短路徑已經被找到。算法重復從結點集 V - S 中算則最短路徑估計的最小的結點 u ,將 u 加入到集合S,然後對所有從 u 出發的邊進行松弛。” 所謂松弛操作,簡單的說就是更新兩點間的最短距離。

不是很好理解對吧,那麽下面的描述是更容易理解的一種描述:

設起始點為s,dis[v]表示s點到v點的最短路徑,pre[v]是v的前驅結點,用來輸出路徑。

1、初始化:dis[v]=∞(v≠s) dis[s]=0,pre[s]=0;

2、for(i=1;i<=n;i++)

(1)在沒有被訪問過的點中,即上述的V - S集合,找到一個點 u 使得dis[u]是最小的。

(2)標記 u 為已確定的最短路徑。

(3)for(每個與 u 相連且沒有確定過最短距離的點 v)

if(dis[u]+m[u][v]<dis[v]){

dis[v]=dis[u]+m[u][v];

pre[v]=u;

}

3、結束:結果dis[v]就是s到v的最短距離。

算法理解:

其中自以為有幾點理解需要說明:

1、為什麽用到中間點?

2、取s到中間點的距離時采用什麽策略?

第一個問題,從起點到另一個點的最短路徑至少會經歷一個中間點,所以我們要求出經過這個中間的到另一個點的路徑,就要先求出起點到中間點的最短路徑。

第二個問題,其實這裏采用的是一中貪心的策略。當然這個策略可以被嚴格證明是正確的,但是我也一知半解,只知道是可以被證明的,在這裏也就不浪費時間了。(詳細可以參考《算法導論》)

最後,解釋一下為什麽有負權邊的時候不可以:

連接矩陣如下(圖可以自己在旁邊畫一下):

1 2 3

1 \ 2 1

2 2 \ -4

3 1 -4 \

那麽第一次標記的點就為3並且把dis[3]記為1,但實際上dis[3]應該時-2,因此就會出現錯誤。

最後附上一段不那麽標準的代碼:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 int m[100][100],e,dist[100],n,b[100],pre[100],dist[100];
 4 void dij(int s){
 5      b[s]=1;
 6      int i,j;
 7      for(i=1;i<=n;i++)
 8       dist[i]=m[s][i];
 9      dist[s]=0;
10      pre[s]=0;
11      
12      for(i=1;i<=n;i++){
13       int min=1000000,k=0;
14       for(j=1;j<=n;j++)
15        if(b[j]!=1 && dist[j]<min)
16         {min=dist[j];k=j;}
17        b[k]=1;
18        for(j=1;j<=n;j++)
19         if(min+m[k][j]<dist[j]&&b[j]!=1)
20          {
21           dist[j]=min+m[k][j];
22           pre[j]=i;
23           }                                
24       }
25       for(i=1;i<=n;i++)
26        if(i!=s)
27         printf("%d ",dist[i]);
28      }
29 int main(){
30     int i,j;
31     scanf("%d%d",&n,&e);
32     memset(b,0,sizeof(b));
33     memset(m,10000,sizeof(m));
34     for(i=1;i<=e;i++){
35      int x,y;
36      scanf("%d%d",&x,&y);
37      scanf("%d",&m[x][y]);
38      }
39     int w;
40     scanf("%d",&w);
41     dij(w);
42     system("pause");
43     return 0;
44     }

圖論(三) (一)最短路徑算法 2.Dijkstra算法