单源最短路径---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);
}

 

posted @ 2018-04-06 16:13  _努力努力再努力x  阅读(11723)  评论(0编辑  收藏  举报