1. 程式人生 > >對比Dijakstra和優先隊列式分支限界

對比Dijakstra和優先隊列式分支限界

eap true lec 先進先出 最短路 分支 同時 In 廣度優先搜索

Dijakstra和分支限界都是基於廣度優先搜索,如果說兩者都是生成一棵樹,那Dijakstra總是找距離樹根最近的(屬於貪心算法),優先隊列式分支限界是在層遍歷整棵搜索樹的同時剪去達不到最優的樹枝。

以下圖為例:求從點s到點t的最短路徑

技術分享圖片

1. Dijakstra

第一步:初始化:將起點s加入集合S,並對所有非集合S的點的距離dist進行初始化(若不與s鄰接,距離為無窮大)

第二步:在非集合S的點集合中,尋找離點s最近的點B(argmin dist[i])並加入集合S,並對所有與B鄰接且非集合S的點i的距離dist進行更新(若dist[i]>dist[B]+W[B][i] 則dist[i]=dist[B]+W[B][i])

第三步:重復第二步,直到在非集合S的點集合中,找不到離點s的距離為有限值的點(這說明存在多棵生成樹)。

主程序如下

while(1){
        x = FindShortest(Graph, collected);
        if(x == -1) break;
        collected[x] = 1;
        for(int i = 0;i<Graph->Nv;i++){
            if(!collected[i] && Graph->L[x][i] != INFINITY){
                
if(dist[i] > dist[x] + Graph->L[x][i]){ dist[i] = dist[x] + Graph->L[x][i]; pri[i] = pri[x] + Graph->P[x][i]; } } } }

2. 優先隊列式分支限界法

分支限界法有隊列式和優先隊列式,兩者區別在於,隊列式只是單純地滿足先進先出,從而實現廣度有限搜索,而優先隊列式是對結點按目標函數值插入一個最大(小)堆,優先處理目標函數值較大(小)的結點。

解決單源最短路徑的步驟:

第一步:初始化,將起點s加入優先隊列(優先隊列的目標函數值為距離dist,建立最小堆),並對所有非集合S的點的距離dist進行初始化(若不與s鄰接,距離為無窮大)

第二步:從優先隊列中取出點,對其所有鄰接結點遍歷,並對距離dist進行更新(若dist[i]>dist[B]+W[B][i] 則dist[i]=dist[B]+W[B][i],並將其加入優先隊列)

第三步:重復第二步,直到隊列為空。

主程序如下(哈哈..這個不是我寫的,看看就好)

while (true) {
     for (int j = 1; j <= n; j++)
       if ((c[E.i][j]<inf)&&(E.length+c[E.i][j]<dist[j])) {
         // 頂點i到頂點j可達,且滿足控制約束
         dist[j]=E.length+c[E.i][j];
         prev[j]=E.i;
         // 加入活結點優先隊列
         MinHeapNode<Type> N;
         N.i=j;
         N.length=dist[j];
         H.Insert(N);}
     try {H.DeleteMin(E);}         // 取下一擴展結點
     catch (OutOfBounds) {break;}  // 優先隊列空
     }
} 

需要註意的是:在第二步中,若不滿足條件dist[i]>dist[B]+W[B][i],則不會對距離進行更新,也不會將其加入優先隊列,這相當於對一個二叉搜索樹的樹枝進行了剪枝

比如對s的鄰接點進行遍歷後,得到隊列為BCA;然後對結點B的鄰接點進行遍歷,得到隊列CAEDF;然後對結點C的鄰接點進行遍歷,首先是結點E,由於當前dist[E]>dist[C]+W[C][E],E被加入優先隊列,且排在上一個E之前,即AE1EDF,而F由於不滿足條件,不會被再次加入優先隊列;同樣地,當對A進行遍歷時,由於不滿足條件,沒有新的結點加入優先隊列(相當於被剪枝),此時隊列裏有 E1EDF,依次進行下去....得到樹如下圖。

圖裏的db可以忽略。。。反正真正跑程序也不會去計算所謂的限界值。

技術分享圖片

對比Dijakstra和優先隊列式分支限界