1. 程式人生 > >Dijkstra[兩種鄰接表+優先佇列優化]

Dijkstra[兩種鄰接表+優先佇列優化]

Dijksta演算法中,如果我們採用的是鄰接矩陣來存的,第一點浪費的空間比較多,第二點我們知道演算法的時間複雜度在O(n*n),這樣的演算法可以說並不是很好,所以我們考慮優化它首先我們可以優化儲存結構,採用鄰接表來儲存,其次我們可以用優先佇列來排序大小,其時間複雜度大大降低。

需要注意的是pair是按照第一個元素的大小排序,如果相同才按照第二個,所以我們要把d[i]包裝在第一個元素上。

vector實現鄰接表+優先佇列 (假設邊一開始是字元型的,這麼假設是為了加點難度)

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
#include<string>
#include<cstring>
#define inf 0x7fffffff
using namespace std;
const int MAXN = 205;
int dis[MAXN];
int n;  //頂點數目
int m;  //邊的條數
string src,ed; //src是源點,ed是目標點
typedef pair<int,int> pii;
struct edge //建立邊的結構體
{
    int u;
    int v;
    int w;
    edge(int a,int b,int c) //建構函式,建立結構體的時候直接呼叫這個函式,方便了一個一個賦值。
    {
        u = a;
        v = b;
        w = c;
    }
};
map<string,int>M; //利用map關聯容器為字串型的邊進行標號
vector<edge> G[MAXN];//鄰接表
void init()
{
    M.clear();//每次迴圈要對map清空
    int cnt=0;
    cin>>n>>m;
    string u,v;//字元型頂點
    int w;//權值
    for(int i=1;i<=m;i++){
        cin>>u>>v>>w;
        if(!M.count(u))
            M.insert(make_pair(u,++cnt));  //make_pair不僅可以用在pair<>的插入中也可用在map,vector等容器中
        if(!M.count(v))
            M.insert(make_pair(v,++cnt));   //利用map關聯容器為字串型的邊進行標號,1,2,3...注意A不一定是1號邊,最先讀入的才是1號邊。
        edge E1(M[u],M[v],w);  //建立無向邊,呼叫建構函式,簡潔。
        edge E2(M[v],M[u],w);
        G[M[u]].push_back(E1); //建立鄰接表
        G[M[v]].push_back(E2);
    }
    cin>>src>>ed;
    for(int i =1;i<=n;i++) dis[i] = (i == M[src] ? 0 : inf);//初始化dis
}
void dijktra()
{
    priority_queue<pii,vector<pii>,greater<pii> > que;
    que.push(make_pair(0,M[src]));//將起點插入佇列,pair預設是優先處理first元素,故插入優先佇列先彈出佇列的優先順序是依據dis[]大小
    while(!que.empty())
    {
        pii tmp = que.top();
        que.pop();
        int x = tmp.second;
        if(tmp.first != dis[x]) continue;     //可避免結點的重複拓展,這裡每一個元素出隊都相當於處理一次已標號結點,如果出隊的這個元素,
                                           //他帶的dis,和當前的dis不相同,證明這個結點是被處理過的,這樣就不需用開一個數組來標記哪些點被處理過了。
        for(int  i = 0;i < G[x].size();i++)
        {
            int y = G[x][i].v;
            int w = G[x][i].w;
            if(dis[y] > dis[x] + w)
            {
                dis[y] = dis[x] + w;
                que.push(make_pair(dis[y],y));
            }
        }
    }
}
int main()
{
   // freopen("1.in","r",stdin);
    int _;
    cin>>_;
    while(_--){
        init();
        dijktra();
        if(dis[M[ed]]==inf) cout<<"-1"<<endl;
        else cout<<dis[M[ed]]<<endl;
        /*輸出源點依次到ABC的距離,不能直接把dis按順序輸出,否則可讀性差,因為A點不一定是1號點,1號點是最先讀入的點,所以利用map以對點A,B,C依排序,*/
        for(map<string,int>::iterator it=M.begin();it!=M.end();it++) printf("%d ",dis[it->second]);
        putchar('\n');
    }
    return 0;
}
輸入資料: 1
6 9
D E 13
B C 9
A B 1
A C 12
B D 3
E C 5
D C 4
D F 15
E F 4
A F
輸出資料: 17
0 1 8 4 13 17

陣列實現鄰接表+優先佇列

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x7fffffff
using namespace std;
const int MAXN = 205;
int dis[MAXN];
int u[MAXN],v[MAXN],w[MAXN];
int first[MAXN],next[MAXN];
int n,m;
int src,ed;
typedef pair<int,int> pii;
void init()
{
    scanf("%d%d",&n,&m);
    memset(first,-1,sizeof(first));
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u[i],&v[i],&w[i]);
        next[i]=first[u[i]];
        first[u[i] ]=i;
        u[i+m]=v[i],v[i+m]=u[i],w[i+m]=w[i];  //無向邊,所以加一條反向邊
        next[i+m]=first[u[i+m] ];
        first[u[i+m] ]=i+m;
    }
    cin>>src>>ed;
    for(int i=1;i<=n;i++) dis[i]= (i==src? 0:inf);//不要把dis[i]初始化成源點到各點的直接距離,否則會有點沒入隊。
}
void dijkstra()
{
    priority_queue<pii,vector<pii>,greater<pii> >que;
    que.push(pii(0,src));
    while(!que.empty()){
        pii tmp=que.top();
        que.pop();
        int x = tmp.second;
        if(tmp.first!=dis[x]) continue;  //如果出隊的這個元素,他帶的dis,和當前的dis不相同,證明這個結點是被處理過的已確定了最短路,
        for(int e=first[x];e!=-1;e=next[e]){   //這種陣列式鄰接表的遍歷
            if(dis[v[e]]>dis[x]+w[e]){
                 dis[v[e] ]=dis[x]+w[e];
                 que.push(pii(dis[v[e] ],v[e]));
            }
        }
    }

}
int main()
{
 //   freopen("1.in","r",stdin);
    int _;
    scanf("%d",&_);
    while(_--){
        init();
        dijkstra();
        if(dis[ed]==inf) cout<<"-1"<<endl;
        else cout<<dis[ed]<<endl;
        for(int i=1;i<=n;i++) printf("%d ",dis[i]);//輸出dist陣列各個值
        putchar('\n');
    }
}

測試資料: 1
6 9
1 2 1
1 3 12
2 3 9
2 4 3
5 3 5
4 3 4
4 5 13
4 6 15
5 6 4
1 6
輸出: 17
0 1 8 4 13 17