首页 > 代码库 > Dijkstra算法优先队列实现与Bellman_Ford队列实现的理解

Dijkstra算法优先队列实现与Bellman_Ford队列实现的理解

 1 /*
 2 Dijkstra算法用优先队列来实现,实现了每一条边最多遍历一次。 要知道,我们从队列头部找到的都是到
 3 已经"建好树"的最短距离以及该节点编号, 并由该节点去更新 树根 到其他点(被更新的节点可以在队列中
 4 ,也可以是非队列中的节点)的距离 。
 5 
 6 ////队列中的节点都是要等待更新的节点 
 7 
 8 如果某个节点从队列中出来的时候,如果cur.first != dist[cur.second] 就是 cur.second这个节点一开始
 9 被更新的最短距离值 和 现在得到的最短距离的值dist[cur.second] 不想等,说明该节点已经被之前队列中
10 具有更短距离的节点更新过了,此时的dist[cur.second] 就是最终的值。 
11 */
12 #include<iostream> 
13 #include<queue>
14 #include<cstring> 
15 #define N 1000
16 using namespace std;
17 
18 class EDGE
19 {
20   public:
21     int u, v, w;
22     int next;//和节点 u 相连的下一条边的编号 
23 };
24 
25 EDGE edge[2*N];
26 
27 typedef pair<int, int>pii;//pair<距离,节点号>
28 
29 int first[N];//最多有N个节点 ,建立每个节点和其相连的边的关系 
30 int dist[N];//源点到各个点的最短距离 
31 
32 int n, m;//节点数,边数 
33 
34 bool operator >(pii a, pii b)
35 {
36    if(a.first==b.first)
37      return a.second > b.second;
38    return a.first > b.first;//按照最短的距离值在队列的前段
39 }
40 
41 priority_queue<pii, vector<pii>, greater<pii> >q; 
42 
43 void Dijkstra()
44 {
45    pii cur;
46    memset(dist, 0x3f, sizeof(dist));
47    dist[1]=0;//另节点 1 为源点
48    q.push(make_pair(0, 1));
49    while(!q.empty()) 
50    {
51       cur=q.top();
52       q.pop();
53       if(cur.first != dist[cur.second])  continue;// 不等于的话说明该节点的值已经经过其他节点松弛为更短的距离值了 
54       for(int e=first[cur.second]; e!=-1; e=edge[e].next)
55         if(dist[edge[e].v]>dist[edge[e].u]+edge[e].w)
56         {
57                dist[edge[e].v]=dist[edge[e].u]+edge[e].w;
58                q.push(make_pair(dist[edge[e].v], edge[e].v));//将更新之后的节点的放入队列之中 
59         }
60    }
61 }
62 
63 int main()
64 {
65    int i;
66    cin>>n>>m;
67    for(i=1; i<=n; ++i)
68      first[i]=-1; 
69    for(i=0; i<m; ++i)
70    {
71        cin>>edge[i].u>>edge[i].v>>edge[i].w;
72        edge[edge[i].u].next=first[edge[i].u];
73        first[edge[i].u]=i;
74    } 
75    Dijkstra();
76    for(i=2; i<=n; ++i)
77      cout<<"1->"<<i<<":"<<dist[i]<<endl; 
78    return 0;
79 } 
 1 /*
 2 Bellman_Ford算法用队列实现和 Dijkstra算法用优先队列来实现相同的地方是,都是 层次 更新到节点的最短距离,
 3 不同的是在Dijkstra()算法中,一个节点只有一次机会进入队列,而Bellman_Ford算法中,每一个节点会有多次进入
 4 队列的机会,为什么呢? 主要是Bellman_Ford算法中实现的是带有负权图的最短距离,因为负权的关系,这样可能使得某个
 5 节点的最短路径的值一直被更新(比如存在负权回路的时候),所以被更新的节点一直会进入队列中 
 6 */
 7 #include<iostream> 
 8 #include<queue>
 9 #include<cstring> 
10 #define N 1000
11 using namespace std;
12 
13 class EDGE
14 {
15   public:
16     int u, v, w;
17     int next;//和节点 u 相连的下一条边的编号 
18 };
19 
20 EDGE edge[2*N];
21 
22 int first[N];//最多有N个节点 ,建立每个节点和其相连的边的关系 
23 int dist[N];//源点到各个点的最短距离 
24 int cnt[N];//记录每个节点在队列中出现的次数 
25 int vis[N];//记录当前的节点是否已经在队列中 
26 
27 int n, m;//节点数,边数 
28 
29 
30 queue<int>q; 
31 
32 int Bellman_Ford()
33 {
34    int cur;
35    memset(dist, 0x3f, sizeof(dist));
36    dist[1]=0;//另节点 1 为源点
37    q.push(1);
38    while(!q.empty()) 
39    {
40       cur=q.front();
41       q.pop();
42       vis[cur]=0;//出队列 
43       ++cnt[cur];
44       if(cnt[cur]>n-1)//如果不存在负权回路,那么某个节点的最多被更新的次数为 n-1 次 
45          return 0;
46       for(int e=first[cur]; e!=-1; e=edge[e].next)
47         if(dist[edge[e].v]>dist[edge[e].u]+edge[e].w)
48         {
49                dist[edge[e].v]=dist[edge[e].u]+edge[e].w;
50                if(!vis[edge[e].v])//本跟新的节点没有在队列中 
51                {
52                   q.push(edge[e].v);//将更新之后的节点的放入队列之中 
53                   vis[edge[e].v]=1;//放入队列
54         } 
55         }
56    }
57    return 1; 
58 }
59 
60 int main()
61 {
62    int i;
63    cin>>n>>m;
64    for(i=1; i<=n; ++i)
65      first[i]=-1; 
66    for(i=0; i<m; ++i)
67    {
68        cin>>edge[i].u>>edge[i].v>>edge[i].w;
69        edge[edge[i].u].next=first[edge[i].u];
70        first[edge[i].u]=i;
71    } 
72    if(!Bellman_Ford())//表示存在负权回路
73       cout<<"不存在最短路径"<<endl;
74    else
75    {
76      for(i=2; i<=n; ++i)
77        cout<<"1->"<<i<<":"<<dist[i]<<endl; 
78    }
79    return 0;
80 }