1. 程式人生 > >淺談路徑規劃演算法之Dijkstra演算法

淺談路徑規劃演算法之Dijkstra演算法

 迪傑斯特拉(dijkstra)演算法是典型的用來解決最短路徑的演算法,也是很多教程中的範例,由荷蘭電腦科學家狄克斯特拉於1959年提出,用來求得從起始點到其他所有點最短路徑。該演算法採用了貪心的思想,每次都查詢與該點距離最的點,也因為這樣,它不能用來解決存在負權邊的圖。解決的問題大多是這樣的:有一個無向圖G(V,E),邊E[i]的權值為W[i],找出V[0]到V[i]的最短路徑。

 演算法的步驟如下:

1)初始化時,S只含有源節點;

2)從U中選取一個距離v最小的頂點k加入S中(該選定的距離就是v到k的最短路徑長度);

 3)以k為新考慮的中間點,修改U中各頂點的距離;若從源節點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值是頂點k的距離加上k到u的距離;

 4)重複步驟(2)和(3),直到所有頂點都包含在S中。

  下面舉個例子來說明這個過程,假設以A為源點。



 dijkstra演算法實現的完整程式碼如下(已測試通過):

#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdlib>
#include<map>
#include<set>
#include<cstdio>
#include<queue>
#include<list>
#include<vector>
#include<stack>
#include<deque>
using namespace std;

const int maxn=107;//點的個數
const int INF=0xfffffff;//不可達的距離

//邊的結構體
struct Edge
{
    int from;
    int to;
    int dist;
    Edge(int u,int v,int d):from(u),to(v),dist(d) {}
};

//迪傑斯特拉的結構體
struct Dijkstra
{
    int n,m;
    vector<Edge> edges;  //存邊
    vector<int> G[maxn]; //存鄰接表
    bool done[maxn];     //是否已經永久標號
    int d[maxn];         //s到各個點的距離
    int p[maxn];         //最短路上的一條弧

    /*優先佇列預設最大堆,即從大到小排列,

    而迪傑斯特拉演算法是尋找的最小路徑,

    所以要選擇過載的<運算子,實現最小堆*/

    struct HeapNode
    {
        int d,u;
        bool operator < (const HeapNode& rhs) const
        {
            return d>rhs.d;
        }
    };

    //初始化,將鄰接表和邊的vector清空
    void init(int n)
    {
        this->n=n;
        for(int i=0; i<n; i++)
            G[i].clear();
        edges.clear();
    }

    //加邊
    void AddEdge(int from,int to,int dist)
    {
        /*
            無向圖需要把這6行全加上

            有向圖只需要加前三行
        */
        edges.push_back(Edge(from,to,dist));
        m=edges.size();
        G[from].push_back(m-1); //push的是下標,因為根據下標找到的資訊更全一些
        edges.push_back(Edge(to,from,dist));
        m=edges.size();
        G[to].push_back(m-1);
    }

    //核心演算法,迪傑斯特拉演算法,S是源點
    void dijkstra(int s)
    {
        priority_queue<HeapNode>Q; //最小堆
        for(int i=0; i<n; i++)
            d[i]=INF;
        d[s]=0;
        memset(done,0,sizeof(done));
        Q.push((HeapNode){0,s}); //建構函式,傳值給d,u,意思是某節點的距離和某節點
        while(!Q.empty())
        {
            HeapNode x=Q.top();
            Q.pop();
            int u=x.u;
            if(done[u])
                continue;
            done[u]=true;
            for(int i=0; i<G[u].size(); i++)
            {
                Edge& e=edges[G[u][i]];
                if(d[e.to]>d[u]+e.dist)
                {
                    d[e.to]=d[u]+e.dist;
                    p[e.to]=G[u][i];
                    Q.push((HeapNode){d[e.to],e.to});
                }
            }
        }
    }
};
int main()
{
    freopen("out.txt","r",stdin); //開啟檔案讀取檔案
    Dijkstra dij;
    int nodenum,edgenum,source; //節點數目、邊的數目、源點
    int from,to,weight;             //一條邊的起點、終點和權值
    cin>>nodenum>>edgenum>>source;

    //初始化邊圖vector
    dij.init(nodenum);

    //加邊
    for(int i=1; i<=edgenum; i++)
    {
        cin>>from>>to>>weight;
        dij.AddEdge(from,to,weight);
    }
    //搜尋
    dij.dijkstra(source);

    //列印每個節點和源點的最短路
    for(int i=0; i<nodenum; i++)
    {
        cout<<"源點到第"<<i<<"個點的最短路是:"<<dij.d[i]<<endl;
    }
    return 0;
}


輸入:第一行三個數字分別是點的個數、邊的條數和源點,後邊9行是9條邊的起點、終點和權值
6  9  0
0  1  6
0  2  3
1  3  5
1  2  2
2  3  3
2  5  4
3  5  2
3  4  3
4  5  5

輸出:

源點到第0個點的最短路是:0
源點到第1個點的最短路是:5
源點到第2個點的最短路是:3
源點到第3個點的最短路是:6
源點到第4個點的最短路是:9
源點到第5個點的最短路是:7