1. 程式人生 > >圖論四:最短路徑演算法

圖論四:最短路徑演算法

一、廣度優先搜尋

1、思路:距離開始點最近的點首先被賦值,最遠的點最後被賦值。

2、適用範圍:對於非負數權的無圈圖來說(單源最短路徑)。

3、演算法實現:

(1)一個佇列記錄每個每個節點的編號。

(2)將起始節點入隊,將所有節點到起始節點的距離設定為無窮大,起始節點到起始節點的距離為0;

(3)取佇列的第一個節點,這個節點出隊,遍歷這個節點相鄰的節點,如果這個節點的距離是INF就變為它前一個節點的距離+1,並且入隊。

(4)重複(3)操作,直到所有所有佇列為空為止,此時dis陣列記錄了每一個節點到起始節點的最小距離。

4、程式碼:

#include<iostream>
#include
<cstdio> #include<queue> #include<vector> using namespace std; const int maxn = 1200; const int INF = 0x3fff; vector <int> vc[maxn]; int dis[maxn]; void bfs(int n) { int i,j,tmp; for(i=1;i<=n;i++) dis[i]=INF; dis[1]=0; queue <int> q; q.push(1); while
(!q.empty()) { tmp=q.front(); q.pop(); for(i=0;i<vc[tmp].size();i++) { if(dis[vc[tmp][i]]>dis[tmp]) { dis[vc[tmp][i]]=dis[tmp]+1; q.push(vc[tmp][i]); } } } } int main(void) {
int n,m,i,x,y; cin>>n>>m; for(i=1;i<=m;i++) { cin>>x>>y; vc[x].push_back(y); } bfs(n); cout<<dis[n]<<endl; return 0; }
View Code

 

5、複雜度:O(|V|^2),複雜度較高。

 

二、Dijkstra演算法

1、思路:貪心

2、適用範圍:有權圖的非負值的無圈圖,解決單源最短路徑的問題(即從st到ed的最短路徑,st確定)。

3、演算法實現:

(1)edge二維陣列表示儲存圖的邊的資訊,(即鄰接陣列儲存圖結構),vis陣列儲存每個節點的狀態

dis儲存每個節點到起始節點的距離,pre儲存每個節點的前一個節點,用來記錄最短路徑。

(2)開始先初始化,pre陣列初始化為-1,設定dis[st]=1(這一步也可以放到dij()函式裡面)。

(3)找到dis中最小值的未被標記過的值,然後標記這個值,找到這個值的鄰接點中可以更新的距離。

(4)重複(3)直到pos為-1。

4、程式碼:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1200;
const int INF = 0x3fff;
//建立圖結構
int edge[maxn][maxn],dis[maxn],pre[maxn];
int vis[maxn],m,n;
void Init()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        edge[i][j]=(i==j?0:INF);
    for(int i=1;i<=n;i++) dis[i]=INF,pre[i]=-1,vis[i]=0;
} 
void dij(int st)
{
    int i,j;
    dis[st]=0;
    for(j=1;j<=n;j++)
    {
        int pos=-1,mi=INF;
        for(i=1;i<=n;i++)
        if(vis[i]==0&&dis[i]<mi)
        {
            mi=dis[i];
            pos=i;
        }
        if(pos==-1) break;
        vis[pos]=1;
        for(i=1;i<=n;i++)
        if(vis[i]==0&&dis[pos]+edge[pos][i]<dis[i])
        {
            dis[i]=dis[pos]+edge[pos][i];
            pre[pos]=i;
        }
    }
}
void Print(int st,int ed)
{
    int x=dis[st];
    printf("路徑是:"); 
    while(st!=-1)
    {
        printf(" %d",st);
        st=pre[st];
    }
    printf("\t最短路徑距離是:%d\n",dis[ed]);
}
int main(void)
{
    int x,y,i,z;
    cin>>n>>m;
    Init();
    for(i=1;i<=m;i++)
    {
        cin>>x>>y>>z;
        edge[x][y]=edge[y][x]=z;
    }
    dij(1);
    Print(1,n);
    return 0;
}
/*
5 5
1 2 3
1 4 5
2 3 4
3 4 1
3 5 7
*/
View Code

 

5、時間複雜度:O(|V|^3)。

 

補充:具有負值邊的圖

1、可以加上一個值變為正數然後再進行dij。

2、直接用廣搜,佇列可以保證不會重複計算。

過程:

(1)將開始的節點放進佇列

(2)每一次取出佇列的頭結點,並查詢它的鄰接節點,尋找比它小的邊的權值。

(3)重複操作直到佇列為空。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 1200;
const int INF = 0x3fff;
int edge[maxn][maxn],vis[maxn],pre[maxn],dis[maxn],n,m;
void Init()
{
    int i,j;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        edge[i][j]=(i==j?0:INF);
    for(i=1;i<=n;i++) pre[i]=-1,dis[i]=INF,vis[i]=0;
    dis[1]=0;
}
void bfs()
{
    queue <int> q;
    q.push(1);
    while(!q.empty())
    {
        int top=q.front();
        q.pop();
        
        for(int i=1;i<=n;i++)
        {
            if(edge[top][i]!=INF&&top!=i&&dis[top]+edge[top][i]<dis[i])
            {
                dis[i]=dis[top]+edge[top][i];
                pre[i]=top;
                if(vis[i]==0) q.push(i),vis[i]=1;
            }
        }
        vis[top]=0;
    }
}
void Print(int st,int ed)
{
    while(st!=-1)
    {
        printf("%d ",st);
        st=pre[st];
    }
    printf("\t%d\n",dis[ed]);
}
int main(void)
{
    int i,j,x,y,z;
    cin>>n>>m;
    Init();
    for(i=1;i<=m;i++)
    {
        cin>>x>>y>>z;
        edge[x][y]=z;
    }
    bfs();
    Print(n,n);
    return 0;
}
/*
5 5
1 2 3
2 3 -4
3 5 7
1 4 5
4 3 -1
*/
View Code

 

三、Floyd演算法

1、思路:動態規劃,狀態轉移方程dw=min(dw,Cv,w);

2、適用範圍:邊權值可以為負數,可以求從任意節點s到其他節點的最短路徑。

3、演算法實現:

(1)設定二維陣列dis(儲存每個節點到其他節點的距離),path(記錄i,j節點之間的中轉節點)。

(2)先初始化,dis陣列賦值為INF,path賦值為j(為中轉做準備)。

(3)三層迴圈,k表示中轉接點,i,j迴圈用來遍歷圖的每一個節點。

(4)可以求出任意兩點之間的最短距離。

4、程式碼:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1200;
const int INF = 0x3fff;
int dis[maxn][maxn],path[maxn][maxn],m,n;
void Init()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) dis[i][j]=INF,path[i][j]=j;
}
void Floyd()
{
    int i,j,k;
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            if(dis[i][j]>dis[i][k]+dis[k][j])
            dis[i][j]=dis[i][k]+dis[k][j],path[i][j]=path[i][k];
}
int main(void)
{
    int x,y,z;
    cin>>n>>m;
    Init();
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y>>z;dis[x][y]=dis[y][z]=z;
    }
    Floyd();
    printf("%d\n",dis[1][n]);
    int st=1,ed=n;
    while(st!=ed) //記錄路徑 
    {
        cout<<st<<" ";
        st=path[st][ed];
    }
    printf("%d\n",ed);
    return 0;
}
/*
5 4
1 2 3
2 3 4
3 4 2
2 5 -1
*/
View Code

 

5、複雜度:O(|V|^3)。