單源最短路徑---Dijkstra算法


傳送門:

Dijkstra

Bellman-Ford

SPFA

Floyd

1、dijkstra算法求解過程:

(1)首先設置兩個頂點集合T和S

  S中存放已找到最短路徑的頂點,初始時,集合S中只有一個頂點,即源點v0

  T中存放當前還未找到最短路徑的頂點

(2)在集合T中選取當前長度最短的一條最短路徑(v0......vk),從而將vk加入到頂點集合S中,並修改遠點v0到T中各個頂點的最短路徑長度;重復這一步驟,直至所有點加入S為止。

2、算法實現

  dist[n]:dist[i]表示當前找到的從源點v0出發到終點vi的最短路徑的長度,初始化時,dist[i] = edge[v0][i]

  S[n]:S[i]為0表示頂點vi還未加入到集合S中,初始化時S[v0]=1,其余為0

  path[n]:path[i]表示v0到vi的最短路徑上頂點vi的前一個頂點序號。采用“倒向追蹤”方法,確定v0到vi的最短路徑上的每個頂點

  初始化:dist[k] = edge[v0][k]v0是源點S[v0]=1

  遞推:
    u = min{dist[t]}s[vt] = 0;

    u表示當前T集合中dist數組元素值最小的頂點的序號,以后u加入集合S。

    dist[k] = min(dist[k], dist[u] + edge[u][k])S[vk] = 0;

https://blog.csdn.net/qq_35644234/article/details/60870719

3.代碼實現:

輸入:

6 9
0 2 5
0 3 30
1 0 2
1 4 8
2 5 7
2 1 15
4 3 4
5 3 10
5 4 18

輸出:

從0到1距離是:20   0->2->1
從0到2距離是: 5   0->2
從0到3距離是:22   0->2->5->3
從0到4距離是:28   0->2->1->4
從0到5距離是:12   0->2->5

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #include<stack>
 8 #include<map>
 9 #include<sstream>
10 using namespace std;
11 typedef long long ll;
12 const int maxn = 1e3 + 10;
13 const int INF = 1 << 30;
14 int T, n, m;
15 int Map[maxn][maxn];//鄰接矩陣存圖
16 int v[maxn];//v[i] = 1表示已加入集合S
17 int d[maxn];//dist[i]表示距離源點的最短距離
18 int path[maxn];//記錄路徑
19 void dijkstra(int u0)//求頂點為u到其他頂點的最短路
20 {
21     memset(v, 0, sizeof(v));
22     for(int i = 0; i < n; i++)d[i] = (i == u0 ? 0 : INF);
23     for(int i = 0; i < n; i++)path[i] = -1;
24     for(int i = 0; i < n; i++)//每次加入一個節點,循環n次
25     {
26         int x, m = INF;
27         for(int y = 0; y < n; y++)if(!v[y] && d[y] <= m)m = d[x = y];//記錄當前最小值
28         v[x] = 1;//標記已經加入集合
29         for(int y = 0; y < n; y++)
30         {
31             if(d[y] > d[x] + Map[x][y])//松弛操作
32             {
33                 d[y] = d[x] + Map[x][y];
34                 path[y] = x;
35             }
36         }
37     }
38     for(int i = 0; i < n; i++)
39     {
40         if(i == u0)continue;
41         printf("從%d到%d距離是:%2d   ", u0, i, d[i]);
42         stack<int>q;
43         int x = i;
44         while(path[x] != -1)
45         {
46             q.push(x);
47             x = path[x];
48         }
49         cout<<u0;
50         while(!q.empty())
51         {
52             cout<<"->"<<q.top();
53             q.pop();
54         }
55         cout<<endl;
56     }
57 }
58 int main()
59 {
60     cin >> n >> m;
61     for(int i = 0; i < n; i++)
62     {
63         for(int j = 0; j < n; j++)
64         {
65             if(i == j)Map[i][j] = 0;
66             else Map[i][j] = INF;
67         }
68     }
69     int x, y, z;
70     for(int i = 0; i < m; i++)
71     {
72         cin >> x >> y >> z;
73         Map[x][y] = z;
74     }
75     dijkstra(0);
76     return 0;
77 }

上述代碼時間復雜度O(n2),這里可以將求最小值用優先隊列優化,時間復雜度降低到了O(nlog(n)),下面用結構體將其封裝起來

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<sstream>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
const int INF = 1 << 30;
int T, n, m;
struct edge
{
    int from, to, dist;
    edge(int u, int v, int d):from(u), to(v), dist(d){}
    edge(){}
};
struct Heapnode
{
    int d, u;//d為距離,u為起點
    Heapnode(){}
    Heapnode(int d, int u):d(d), u(u){}
    bool operator <(const Heapnode & a)const
    {
        return d > a.d;//這樣優先隊列先取出d小的
    }
};
struct Dijkstra
{
    int n, m;
    vector<edge>edges;//存邊的信息
    vector<int>G[maxn];//G[i]表示起點為i的邊的序號集
    bool v[maxn];//標記點是否加入集合
    int d[maxn];//起點s到各個點的最短路
    int p[maxn];//倒敘記錄路徑
    Dijkstra(){}
    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)
    {
        edges.push_back(edge(from, to, dist));
        m = edges.size();
        G[from].push_back(m - 1);//存以from為起點的下一條邊
    }
    void dijkstra(int s)//以s為起點
    {
        priority_queue<Heapnode>q;
        for(int i = 0; i < n; i++)d[i] = INF;
        d[s] = 0;
        memset(v, 0, sizeof(v));
        memset(p, -1, sizeof(p));
        q.push(Heapnode(0, s));
        while(!q.empty())
        {
            Heapnode now = q.top();
            q.pop();
            int u = now.u;//當前起點
            if(v[u])continue;//如果已經加入集合,continue
            v[u] = 1;
            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];//記錄e.to前的邊的編號,p存的是邊的下標,這樣可以通過邊找出之前的點以及每條路的路徑,如果用鄰接矩陣存儲的話這里可以直接存節點u
                    q.push(Heapnode(d[e.to], e.to));
                }
            }
        }
    }
    void output(int u)
    {
        for(int i = 0; i < n; i++)
        {
            if(i == u)continue;
            printf("從%d到%d距離是:%2d   ", u, i, d[i]);
            stack<int>q;//存的是邊的編號
            int x = i;//x就是路徑上所有的點
            while(p[x] != -1)
            {
                q.push(x);
                x = edges[p[x]].from;//x變成這條邊的起點
            }
            cout<<u;
            while(!q.empty())
            {
                cout<<"->"<<q.top();
                q.pop();
            }
            cout<<endl;
        }
    }
};
Dijkstra ans;
int main()
{
    cin >> n >> m;
    ans.init(n);
    for(int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        ans.addedge(u, v, w);
    }
    int u = 0;
    ans.dijkstra(u);
    ans.output(u);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM