最短路的四种算法

来源:互联网 发布:java流程控制 编辑:程序博客网 时间:2024/06/05 11:06

求最短路一般有四种方法:

1.floyd       dp思想dis[i][j]=min(dis[i][j]+dis[i][k]+dis[k][j])                                 时间复杂度O(N^3)
2.dijkstra    邻接矩阵。                                                                                  时间复杂度O(N^2)
3.dijkstra    邻接表+优先级队列优化。                                                           时间复杂度O(M*lgN)
4.bellman-Ford 用于解决含有负权值得最小路问题                                        时间复杂度O(M*N)    
5.SPFA是bellman-ford的改进算法(队列实现),效率也更高                         复杂度为:O(kE)E为边数,k一般为2或3
1.floyd算法:可以求任意两点之间的最短距离。解决传递背包和最小环问题~,不能解决负权回路。
for(int k=0;k<a;k++)//经过k点{    for(int i=0;i<a;i++)//起点    {        for(int j=0;j<a;j++)//终点        {            If(w[i][j]>w[i][k]+w[k][i];            w[i][j]=w[i][k]+w[k][i];        }     }}//如果经过k点距离变短,那么w[i][j]不变,否则更新。
可以理解为对于1点而言,经过或者不经过1点求个最短,然后在1点的基础上对于2点,求个最短路...。
2.dijkstra 邻接矩阵:没办法解决负边权的最短路径。
采用的是邻接矩阵来存的,第一点浪费的空间比较多,第二点我们知道算法的时间复杂度在O(n*n)

(1)确定数组—邻接矩阵(赋值0或者inf)

(2)输入数组-邻接矩阵

(3)确定距离dis数组//用1号顶点到其余各边的距离初始化;

(4)判断某一点是否当过起始点bool数组

(5)最关键!!循环n-1次,

          先找一行中的最小值确保该行没有遍历过。

         找到之后通过最小值所在行更新dis数组。

      //初始化    for(i=1;i<=n;i++)        for(j=1;j<=n;j++)            if(i==j) e[i][j]=0;                else e[i][j]=inf;                  //读入边和权值    for(i=1;i<=m;i++)    {        scanf("%d %d %d",&t1,&t2,&t3);        e[t1][t2]=t3;    }    //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程    for(i=1;i<=n;i++)        dis[i]=e[1][i];    //book数组初始化    for(i=1;i<=n;i++)        book[i]=0;    book[1]=1;        //Dijkstra算法核心语句    for(i=1;i<=n-1;i++)//循环n-1次,每次都找到一行中的最小值,也就是对应的最小的点。    {        min=inf;        for(j=1;j<=n;j++)//找离某一点最近的点        {            if(book[j]==0 && dis[j]<min)            {                min=dis[j];                u=j;            }        }        book[u]=1;        for(v=1;v<=n;v++)//找到最小点的最小距离的点,有的话就更新。        {            if(e[u][v]<inf)            {                if(dis[v]>dis[u]+e[u][v])                     dis[v]=dis[u]+e[u][v];            }        }    }

3.dijkstra    邻接表+优先级队列优化。
重载一个比较函数
struct node{      int x,d;      node(){}      node(int a,int b){x=a;d=b;}      bool operator < (const node & a) const      {          if(d==a.d) return x<a.x;          else return d > a.d;      }  };  //定义一个结构体,重载比较函数
  1. vector<node> eg[Ni];  
  2. int dis[Ni],n;  
void Dijkstra(int s)  {      int i;      for(i=0;i<=n;i++) dis[i]=INF;      dis[s]=0;      //用优先队列优化      priority_queue<node> q;      q.push(node(s,dis[s]));      while(!q.empty())      {          node x=q.top();q.pop();          for(i=0;i<eg[x.x].size();i++)          {              node y=eg[x.x][i];              if(dis[y.x]>x.d+y.d)              {                  dis[y.x]=x.d+y.d;                  q.push(node(y.x,dis[y.x]));              }          }      }  }  

4.bellman-Ford
一:初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。二:进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。三:遍历途中所有的边(edge(u,v)),判断是否存在这样情况: d(v) > d (u) + w(u,v) 则返回false,表示途中存在从源点可达的权为负的回路。
struct node{    int from,to,w;};node edge[100];bool Ford(){    for(i = 1; i <= n; i ++)        dis[i] = inf;    dis[1] = 0;    for(i = 1; i <= n; i ++)    {        flag = false;        for(j = 1; j <= m; j ++)        {            x = edge[i].from ;            y = edge[i].to ;            z = edge[i].w ;            if(dis[y] > dis[x] + z)            {                dis[y] = dis[x] + z;                flag = true;            }        }        if(!flag)            break;        //如果更新到n遍,还能够继续更新dis数组,说明存在负权环         if(flag&&i == n)             return false;//返回false表示这个图存在负权环     }    return true;//返回true表示求最短路成功 }
5.SPFA


原创粉丝点击