日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单源最短路径---Dijkstra算法

發布時間:2024/5/14 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单源最短路径---Dijkstra算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

有這樣一道題:在一個圖(如圖所示)中,一共有四個點:1 2 3 4

這四個點之間各有相連,且每條邊都有自己的權值。現在小明在點1上,

他想要到3去,請問最短路徑是多少。



很容易得到該圖的鄰接矩陣。我們建立一個二維數組a。a[i][j],i表示
起點,為行,j表示終點,為列。將相應的權值傳入其中,如果從一個
點到另一個點不通,就認為其權值為無限

例如(1-》2)為2,則a[1][2]=2;
而因為2到1不通,就令a[2][1]=∞;
另外a[1][1]之類的起點終點相同的都設為了0;

對這種求點i到點j的最短路徑的問題,很難直接求得。通常是要多求一
些多余的量才能得到結果。因為我們必須要做好遍歷全圖的準備才能比
較或是尋找出其最短路徑。


這時候我們來介紹兩種算法:

  • 一種是Dijkstra算法單源最短路徑算法
  • 一種是Floyd多元路徑最短算法

今天只說單源最短路徑算法
所謂單源,即求從一個點出發,到其他各點的最短路徑,也就是說
如果這個圖有n個點,我們要求n-1個路徑。
對一個圖G來說,它的點集為V,我們要做的就是求出從起點v到V中其
余各點的最短路徑。

首先介紹單源最短路徑的核心算法:
 起點是v,我們知道在整個圖中,以v為起點的路徑有很多條,有長的。有短的;有的簡單,只有一段弧,只有兩個點:起點。終點。有的復雜,有好幾段弧,起點終點之間隔了好幾個節點。
 我們要做的就是先找到所有這些從v引出的路徑中的最短的那一條(v,…,j1)。
 然后通過某種算法(下文中將會介紹)再找出僅長于最短路徑的次短路徑(v,…,j2),找到以后,(注意:我們把每一次找到的次短路徑的終點記錄下來,如果下一次找尋的時候遇到以這些點為終點的路徑就忽略掉),再找下一條次短路徑,再找,再找……
  這里有一個辨析:

按照上述步驟,我們依次找到了整張圖中(具有不同終點的)最短的幾條路徑: (v,…,j1) (v,…,j2) (v,…,j3) …… (v,…,j) ……

這里,如果我們稱圖中所有以v為起點 以j為終點的路徑為j族,那么我們每找到一條次短路徑 (v,…,j),這條路徑(v,…,j)一定是j族中最短的那條。
為什么呢?按照我們的找法,我們實際上是把整張圖里的所有以v為起點的路徑按照從短到長的順序排列起來,然后從第一條路徑開始往后檢索,如果我們從前到后查找時第一次遇到了以j為終點的路徑,記為路徑1,接下來往后,我們遇到的每一條屬于j族的路徑都忽略,從而我們得到了之前的序列。顯然路徑1就是j族中最短的那一條,也是我們的目標路徑之一。


我們 所要求的是點v到其余各點的最短路徑,其余各點有n-1個,也就是說,我們有n-1個終點,對應n-1個族。對每個族,都有一個對應的最短路徑。我們要做的就是求出每個族的最短路徑。

之前說到,我們要把每一次找到的次短路徑的終點記錄下來,下一次遇到的時候就忽略掉,這可以保證最終的得到的序列都屬于不同的族,我們可以建立一個點集S,每找到一條次短路徑,就將其終點并入集合S中,下一次找尋路徑的時候拿終點與點集S對比,決定是否保留。

就這樣,每一次我們都找到一個以j為終點的最短路徑,而每一次的終點j又都不同,當找了n-1次時,就完成了單源最短路徑查找。


我們已經了解完了整體的思路,所以現在的關鍵性問題就是如何完成對次短路徑的查找。也就是上文中提到的某種算法,它到底是什么呢?

  • 第一步:

      一個圖中有許多條路徑,我們現在在圖G中找到以v為起點的最短的那一條路徑(v,j)。顯然我們可以很容易的的發現,這條最短路徑一定是與起點v直接相連的弧,j是v的鄰結點。如圖所示;
      

  • 第二步:
     接下來,我們來找次短路徑,也就是以v為起點的下一條最短的路徑。如圖所示我們會發現,次短路徑要么是弧(v,k)(注:k是v相鄰的點,除j外),要么是弧(v,j,k)(k是j緊鄰的點)。
     這樣,我們通過比較可以求出最短的那一條路徑(v,k);

然后我們就會猜想,如果按照第二步的做法一直重復下去,不就能依次找到次短路徑了嗎?
但是,事實上,當我們嘗試之后會發現,隨著不斷地重復查找次短路徑的過程,我們不能單純的像第二次查找那樣,因為每一次的查找都會衍生很多分支。使得查找變得復雜。


怎么解決這個問題呢?為了方便理解我引入了一個觀測域的概念。

首先看一些定義:

  • 點集V 圖中所有點的集合
  • 點集S  已經找到相應最短路徑的終點集合
  • 數組D[n] 存儲觀測域內能觀測到的最短路徑,算上起點一共n個數值 。比如D[k]對應在觀測域中能觀測到的,k族中的最短路徑。
  • 鄰接矩陣a[n][n]  存儲著相應的權值

如圖所示:剛開始時,我立足于v點,觀測域為v點的四周,即v的所有鄰接點。由鄰接矩陣a,更新數組D。此時D中的數為(v,k)的權值(k為v的鄰接點)。
 


隨后,我在我觀測域中找尋最短的那一條路徑(v,j)也就是查找數組D中最小的數,并將j收入集合S中。

如圖所示:現在我想找次短路徑,現在我清楚,這條次短路徑要么是(v,k)(k為觀測域中的點,但不屬于S)的最短邊,要么是通過j點的弧(v,j,k)(k為j的鄰接點,但不屬于S)的最短邊。


我們干脆將j的鄰接點全都納入觀測域,同時更新數組D,通過數組D找出次短邊(v,j),再將j壓入S中。

不斷重復該步驟,直到所有的點都入了S,就完成了查找,這時數組D
D[k]就是從v到k的最短路徑的長度。如果你想知道具體的路徑時只需加個棧就行了。


所以完整的步驟是這樣的:

  • 第一步:
    初始化點集S,將起點v收入S中。初始化數組D:D[k]=a[v][k];

  • 第二步:找尋次短路徑。即查找數組D找出觀測域中最短路徑(v,j):D[j]=min(D[k]|k不屬于S)。將j壓入點集S中

  • 第三步:將j的鄰接點并入觀測域,即用j的鄰接點更新數組D:

    如果D[k]>D[j]+a[j][k] (k為j鄰接點,k不屬于S)令D[k]=D[j]+a[j][k]如果D[k]>D[j]+a[j][k] (k為j鄰接點,k不屬于S)就不做操作

然后不斷重復第二步和第三步直到找到全部節點為止。

具體實現為:

#include<iostream> using namespace std; #define BUTONG 1000000 // 無限大為不通 #define NUM 4 //d點數為4 int a[NUM][NUM]={0,2,6,4,BUTONG,0,3,BUTONG,7,BUTONG,0,1,5,BUTONG,12,0}; #define OK 1 #define ERROR 0 typedef int status;bool finish(bool *S,int n) //是否完成 找尋 {for(int i=0;i<n;i++){if(!S[i])return false;}return true; }status djs(int n,int t)//a 為數組 n 為點的個數,v為起始點 {//初始化數組vint D[n];for(int i=0;i<n;i++)D[i]=a[t][i];D[t]=0;//初始化已訪問數集S;bool S[n];for(int i=0;i<n;i++)S[i]=false;S[t]=true;int j=0;int min=BUTONG;while (!finish(S,n)){ j=0;min=BUTONG;for(int i=0;i<n;i++) //找到觀測域中最短路徑(v,j) {if(S[i]) continue;if(min>D[i]) {min=D[i];j=i;} } S[j]=true; //將j納入點集S中 for(int i=0;i<n;i++) //更新觀測域 {if(S[i]) continue;if(D[i]>D[j]+a[j][i])D[i]=D[j]+a[j][i]; }}for(int i=0;i<n;i++) //輸出 {printf("最短路徑是(v,%d)長度是%d\n",i,D[i]);}return OK; } int main() {djs(NUM,1);return 0;}

總結

以上是生活随笔為你收集整理的单源最短路径---Dijkstra算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。