1. 程式人生 > >dijkstra求最短路並記錄路徑

dijkstra求最短路並記錄路徑

dijkstra演算法是單源最短路演算法的一種,可用於求從出發節點到所有可到達節點的最短路長度。

下面我們來看下如何記錄最短路的路徑,有關dijkstra求最短路長度的過程,可以看這篇部落格“dijkstra求最短路徑長度”。

舉例

下圖中直接給出記錄路徑的過程,相比於求解最短路長度,只多了一個path陣列(初始化為-1)用於記錄路徑。

分析

從上圖可以看到,從出發節點0到節點5取得最小長度65,所經過的路徑是0,3,2,4,5。

圖中通過path陣列來記錄路徑,path[i]=j表明節點i取得最小路徑時,其最後一段走的是節點j到節點i。

你也許會疑惑,我想知道的是整個路徑呀,記錄其中的最後一段有什麼用呢?

  • 我們這樣來看,path[5]=4表明0->5的最短路徑最後走的一段是4->5;同理path[4]=2確定0->4的最短路徑的最後一段是由節點2到達節點4;那麼通過path[2]=3可以得到0->2最短路徑最後一段是由節點3到達節點2;而path[3]=-1表示從出發節點0有一條直接路徑連線到節點3。在這個過程中,我們獲得經過的路徑是倒序的,所以給出答案時需要反轉,故從出發節點0到節點5所經過的最短路徑是0,3,2,4,5。

但你也許又有一個疑問,path[5]=4表明0->5的最短路徑最後一段是4->5,可是你怎麼知道0->4的最短路徑與0->5的最短路徑在0->4段走的是相同的路徑呢?

  • 首先0->5的最短路徑是0,3,2,4,5,path[5]=4表明0->5最短路徑的最後一段是4->5,其中4->5必定只有一條直接路徑,所以可以推得0->4的最短路徑為0,3,2,4;因為如果0->4有更短的路徑0->X->4,那麼0->5的最短路徑必定也會變成0->X->4->5。
  • 從圖中也可以看到,path[i]並不是一旦賦值就不會改變的;只有i被作為了中途節點,那麼path[i]才不會再改變,即出發節點到節點i的最小長度被確定後。

注意:圖中初始化時path全為-1,並不是因為其出發節點為-1。比如path[i]=-1表明的是出發節點到節點i是直接路徑,沒有中途節點;並不代表從節點-1可以到節點i,也不代表-1是出發節點。當然初始值可以任意替換,只要注意更改“while(path[j]!=-1)”即可。

程式碼實現

#include<stdio.h>
#include<string.h>
#include<stack>
using namespace std;
const int N=100;
const int INF=100000;
int p[N][N],d[N],path[N];       //path陣列用於記錄路徑

void dijkstra(int sec,int n)    //sec為出發節點,n表示圖中節點總數
{
    int i,j,min,min_num;
    int vis[N]={0,};
    for(i=0;i<n;i++)
    {
        d[i]=p[sec][i];
    }
    vis[sec]=1;d[sec]=0;
    for(i=1;i<n;i++)
    {
        min=INF;
        for(j=0;j<n;j++)
        {
            if(!vis[j]&&d[j]<min)
            {
                min=d[j];
                min_num=j;
            }
        }
        vis[min_num]=1;
        for(j=0;j<n;j++)
        {
            if(d[j]>min+p[min_num][j])
            {
                path[j]=min_num;//path[j]記錄d[j]暫時最短路徑的最後一箇中途節點min_num,表明d[j]最後一段從節點min_num到節點j
                d[j]=min+p[min_num][j];
            }
        }
    }
}
void print(int sec,int n)       //sec為出發節點,n表示圖中節點總數
{
    int i,j;
    stack<int> q;               //由於記錄的中途節點是倒序的,所以使用棧(先進後出),獲得正序
    for(i=1;i<n;i++)            //列印從出發節點到各節點的最短距離和經過的路徑
    {
        j=i;
        while(path[j]!=-1)      //如果j有中途節點
        {
            q.push(j);          //將j壓入堆
            j=path[j];          //將j的前箇中途節點賦給j
        }
        q.push(j);
        printf("%d=>%d, length:%d, path: %d ",sec,i,d[i],sec);
        while(!q.empty())       //先進後出,獲得正序
        {
            printf("%d ",q.top());//列印堆的頭節點
            q.pop();            //將堆的頭節點彈出
        }
        printf("\n");
    }
}
int main()
{
    memset(path,-1,sizeof(path));//將path陣列初始化為-1
    int i,j,n=6;
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            p[i][j]=(i==j?0:INF);
        }
    }
    p[0][1]=10;p[0][3]=30;p[1][2]=50;p[1][4]=100;p[2][4]=5;p[3][2]=20;p[3][4]=60;p[4][5]=10;//p[i][j]表示節點i到節點j的距離
    dijkstra(0,n);               //求從節點0出發到各節點的最短距離
    print(0,n);                  //列印從節點0出發到各節點的最短距離和路徑
    return 0;
}

程式碼執行結果: