图论之单源最短路径问题

单源最短路径问题

问题描述

对一幅图G,我们对每一条边赋权w(e),成为一个赋权图。H是G的一个子图,则W(H) = sigma(w(e)),也就是对每条边的权求和。寻找从一个点a到另一个b的一个子图,使得权和最小,即为最短路问题。

Dijkstra(迪杰斯特拉算法算法):

  • 把结点集分割为二子集S,T.开始时S={a},T=V-S.
  • 每结点t∈T,求出D(t,a)之后再定出x∈T使得D(x)= min{D(t,a)t∈T}.
  • 置S为S∪{x}置T为T-{x}.若T=O则停止,否则转(2)作下一次循环.
    用白话讲,迪杰特斯拉算法就是每次确定一个最短距离点,并用这个点去更新与之相连的其他边

算法流程

1.先标记起点,然后从起点出发,得出到各个点的最小距离。
2.再每次找出还没有讨论过的离起点最近的那个点,从这个点出发,将前面得到的起点到各个点的最短路径和以这个点为中介点,再到各个点的距离大小作比较,取最小值。
时间复杂度o(n2)。

代码

dij(){
for(int I=1;i<=n;i++){
int min=INF;
int u=0;
for(int v=1;v<=n;v++){
    if(vis[v]==false&&dis[v]<min){//在没有访问过的点中选取最小的
            min=dis[v];
            u=v;
      }
}
if(u==0)
break;
vis[u]=true;
for(int v=1;v<=n;v++)
{
      if(dis[u]+w[u][v]<dis[v]){
            dis[v]=dis[u]+w[u][v];//更新
            pre[v]=u;//记录前驱
      }
}

Floyd算法

插点法
dis[I][j]为i、j两点的距离。w[i][j]为i、j权值
若u、v有边,dis[u][v]=w[u][v]
否则dis[u][v]=0x3f3f3f3f(极大值)

代码

for(int k=1;k<=n;k++)
      for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
            if(dis[i][j]>dis[i][k]+dis[k][j])
            dis[i][j]=dis[i][k]+dis[k][j];
}

Ford 算法

使用动态规划的想法,通过中介点。每次更新所有的边,从而确定一个点的最短距离。
其时间复杂度是 O(NNN),N是顶点数。

算法流程

-起始时,认为起点是白点(dis[1]=0),每一次都枚举所有的边,必然会有一些边,连接着白点和蓝点。
-用所有的白点去修改所有的蓝点,每次循环也必然会有至少一个蓝点变成白点。

代码

S为起点dis[v]表示从S到v的最短路径。u[i]和v[i]链接u(起点)v(终点)的边i的长度。

for(int i=0;i<n;i++)
dis[i]=INF;
dis[0]=0;
for(int i=1;i<=n-1;i++){
for(int j=1;j<=m;j++){
int x=u[j];//得到j起点
int y=v[j];//得到j终点
if(dis[x]<INF)
dis[y]=min(dis[y],dis[x]+dis[x]+w[j]);
}
}

SPFA 算法

实质是 Ford 算法加了判断负环的队列实现版本。
其利用队列以进行 Ford 算法的过程,初始时将起点加入队列,每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若相邻的点修改成功,则将其入队,直到队列为空时算法结束。
SPFA 时间复杂度可达:O(k*E),其中,E 是边数,k 是常数,平均值是 2。

posted @ 2020-10-13 10:44  小帆敲代码  阅读(65)  评论(0编辑  收藏  举报