Dijistra求最短路(邻接表存储,前向星存储,堆优化)

来源:互联网 发布:java 写二进制文件 编辑:程序博客网 时间:2024/06/06 17:46

dijistra算法主要用来求稀疏图的单源点最短路径,其核心是首先存储单源点到其它各点的路径,叫做估计最短径值,然后找出其中最短的一条路径,记录该点,那么此时该点的估计最短径值就变成了确定最短路径值。

例如:这里同dis数组来记录单源点1到其它各点最短路径值,下标从1开始,最初其值为 0 5 1 3 4.

此时得到dis[3] = 1为最小值,则可以确定源点到点3的最短路径,另外点3到点2有一条路径长度为2,容易知道,1+2=3 > 5,则此时可松弛点2的最短路径变为3。此时大致的思路已经出来了。


先来一个邻接矩阵存储边信息的Dijistra,

#include <iostream>#include <cstdio>#define inf 10e7using namespace std;int main(){int a, b, c, k, i, j, n, m, l, min, jk[100][100], dis[100], book[100]={0};cin >> n >> m;for(i = 1; i <= n; ++i)for(j = 1; j <= n; ++j)if(i == j) jk[i][j] = 0;else jk[i][j] = inf;for(i = 1; i <= m; ++i){cin >> a >> b >> c;jk[a][b] = c;}for(i = 1; i <= n; ++i)dis[i] = jk[1][i];book[1] = 1;for(k = 1; k < n; ++k){min = inf;for(i = 1; i <= n; ++i)if(book[i] == 0 && min >= dis[i]) {min = dis[i];l = i;}book[l] = 1;for(i = 1; i <= n; ++i)if(jk[l][i] < inf && dis[i] > dis[l]+jk[l][i])dis[i] = dis[l]+jk[l][i];}for(i = 1; i <= n; ++i)printf("%d ", dis[i]);printf("\n");return 0;}

再来一个数组实现的邻接表的Dijisra,

#include <iostream>#include <cstring>#include <cstdio>#define inf 10e7using namespace std;int u[100], v[100], w[100], head[100], next2[100], dis[100], book[100]={0};int main(){int k, i, j, n, m, l, min;cin >> n >> m;memset(head, -1, sizeof(head));for(i = 1; i <= m; ++i){cin >> u[i] >> v[i] >> w[i];next2[i] = head[u[i]];head[u[i]] = i;}for(i = 1; i <= n; ++i)dis[i] = inf;dis[1] = 0;k = head[1];while(k != -1){dis[v[k]] = w[k];k = next2[k];}book[1] = 1;for(j = 1; j < n; ++j){min = inf;for(i = 1; i <= n; ++i)if(book[i] == 0 && min >= dis[i]) {min = dis[i];l = i;}book[l] = 1;k = head[l];while(k != -1){if(w[k] < inf && dis[v[k]] > dis[l]+w[k])dis[v[k]] = dis[l]+w[k];k = next2[k];}}for(i = 1; i <= n; ++i)printf("%d ", dis[i]);printf("\n");return 0;}

别小看了邻接表和邻接矩阵的差别,有时候题目是否可以AC就在于次,邻接矩阵空间复杂度为O(n^2),邻接表空间复杂度为O(M),对于稀疏图来说,两者的差距可想而知。

所以,再来一个类似邻接表的存储方式——前向星存储,它们的空间复杂度是差不多,

#include <iostream>#include <cstring>#include <cstdio>#define inf 10e7using namespace std;int no, head[100], dis[100], book[100]={0};struct node{int to, w, next;} edge[100];void add(int u, int v, int w){edge[no].to = v;edge[no].w = w;edge[no].next = head[u];head[u] = no++;}int main(){int a, b, c, k, i, j, n, m, l, min;cin >> n >> m;no = 1;memset(head, -1, sizeof head);for(i = 1; i <= m; ++i){cin >> a >> b >> c;add(a, b, c);}for(i = 1; i <= n; ++i)dis[i] = inf;dis[1] = 0;k = head[1];while(k != -1){dis[edge[k].to] = edge[k].w;k = edge[k].next;}book[1] = 1;for(j = 1; j < n; ++j){min = inf;for(i = 1; i <= n; ++i)if(book[i] == 0 && min >= dis[i]) {min = dis[i];l = i;}book[l] = 1;k = head[l];while(k != -1){if(edge[k].w < inf && dis[edge[k].to] > dis[l]+edge[k].w)dis[edge[k].to] = dis[l]+edge[k].w;k = edge[k].next;}}for(i = 1; i <= n; ++i)printf("%d ", dis[i]);printf("\n");return 0;}


既然我们学到了图论,想必已经学过单链表了,所以博主无聊又用链式邻接表来实现了一番,

#include <iostream>#include <cstdlib>#include <cstring>#include <cstdio>#define inf 10e7using namespace std;struct node{int v, w;node *next;}*jk[105], *k, *pre;int dis[100], book[100];int main(){int n, m, u, v, w, min, i, j, l;memset(book, 0, sizeof book);cin >> n >> m;for(i = 1; i <= n; ++i)jk[i] = NULL;for(i = 1; i <= m; ++i){cin >> u >> v >> w;k = jk[u];pre = NULL;while(k != NULL) {pre = k;k = k->next;}k = (struct node*)malloc(sizeof(struct node));k->v = v;k->w = w;k->next = NULL;if(pre == NULL) jk[u] = k;else pre->next = k;// k = jk[v];//如果是无向图,应加上这么一块// pre = NULL;// while(k != NULL) {// pre = k;// k = k->next;// }// k = (struct node*)malloc(sizeof(struct node));// k->v = w;// k->w = w;// k->next = NULL;// if(pre == NULL) jk[v] = k;// else pre->next = k;}for(i = 1; i <= n; ++i)dis[i] = inf;dis[1] = 0;k = jk[1];while(k != NULL){dis[k->v] = k->w;k = k->next;}book[1] = 1;for(j = 1; j < n; ++j){min = inf;for(i = 1; i <= n; ++i)if(book[i] == 0 && min >= dis[i]) {min = dis[i];l = i;}book[l] = 1;k = jk[l];while(k != NULL){if(dis[k->v] > dis[l]+k->w)dis[k->v] = dis[l]+k->w;k = k->next;}}for(i = 1; i <= n; ++i)printf("%d ", dis[i]);printf("\n");return 0;}


最后,就是我们的终极形态了,前向星存储+堆优化的DIjistra,为什么要用到堆优化呢,因为我们每次寻找最短的路径值的时候,需要花费O(N)的时间去寻找,用堆可优化至O(logN),也可别小瞧这俩之间的差距。一提到这儿,我又想起了昨天的比赛,就有这么一道最短路的题,刚开始我和队友用的是队列优化的Bellman-ford算法即SPFA,博主比较喜欢这个算法,但昨天的比赛的数据点卡住SPFA了,昨天的题是在第49个点上TLE(超时)的,用堆优化的Dijistra才AC了那道题...博主才学疏浅,希望神牛不要嘲笑,好,下面贴出我们的代码,

#include <iostream>  #include <string.h>  #include <cstdio>  #include <queue>  #define inf 0x3f3f3f3f  using namespace std;  int head[100], dis[100], book[100], pre[100];  struct note{      int v, w, next;  } edge[100*100];  struct node{      int v, w;      node(int a, int b){          w = a;          v = b;      }      friend bool operator< (node a, node b){          return a.w > b.w;      }  };  int n, m, no;  priority_queue<node> q;  void add(int u, int v, int w){      edge[no].v = v;      edge[no].w = w;      edge[no].next = head[u];      head[u] = no++;  }  void init(){      no = 0;      memset(head, -1, sizeof head);    memset(dis, 0x3f, sizeof dis);    memset(book, 0, sizeof book);    memset(pre, -1, sizeof pre);}  void Dijkstra(){      while(!q.empty()) q.pop(); //将声明放在bss区(全局区),减少栈内存的消耗,此句清空队列中数据       dis[1] = 0;      q.push(node(0, 1));      while(!q.empty()){          node x = q.top();          q.pop();          if(book[x.v]) continue;          book[x.v] = 1;          int k = head[x.v];          while(k != -1){              if(dis[edge[k].v] > dis[x.v]+edge[k].w){                  dis[edge[k].v] = dis[x.v]+edge[k].w;                pre[edge[k].v] = x.v;                  q.push(node(dis[edge[k].v], edge[k].v));              }              k = edge[k].next;          }      }  } void PrintPath(int end){int k = end;while(k != -1){printf("%d ", k);k = pre[k];}printf("\n");} int main(){      int a, b, c, i;      scanf("%d %d", &n, &m);     init();      for(i = 1; i <= m; ++i){          scanf("%d %d %d", &a, &b, &c);          add(a, b, c);   //无向图           add(b, a, c);      }      Dijkstra();       for(i = 1; i <= n; ++i)          printf("%d ", dis[i]);      printf("\n");      PrintPath(5);//打印路径    return 0;  }/*样例:5 51 2 21 3 32 4 43 4 24 5 1*/

最后一份代码是最终的Dijistra了,如果大家想了解适合稠密图的floyed-Warshell算法,可看上一篇文章,想了解Bellman-ford算法及其队列优化的朋友可看下一篇文章。


1 0
原创粉丝点击