最短路算法汇总

1、Dijkstra

dijkstra算法的思想是一种贪心的思想,它求得是单源最短路的问题,设置一个顶点的集合,初始集合是源点,然后从源点出发,找到最近的点把该点纳入这个集合,然后再从这个集合出发,把离源点最近的点纳入集合,直到把所有的点纳入集合。最后的到的即是从源点出发到其它所有的点的最短距离。

举个例子:

求1到8的最短距离。我们用一个集合G来代表点集,初识G{ },然后d[]数组代表其它点到1的距离,初始d[1]=0,d[>1]=inf表示还没确定。

第一轮,把1加入G集合,G{1},更新与1相邻的点。d[2]=2,d[4]=1,d[6]=3,其它还是inf

第二轮,选择到源点最短距离的点,即4,把它并入G{1,4},更新

d[2]=min{d[2],d[4]+map[4][2]},d[2]=2;

d[6]=min{d[6],d[4]+map[4][6]},由于map[4][6]=inf,所以d[4]=3;

d[7]=min{d[7],d[4]+map[4][7]},d[7]=3;

第三轮,选择到源点最近的点,即2点,然后如上更新。

。。。。。。

最后:

G{1,2,3,4,5,6,7,8},图中所选的路径既是到所有点的最短路径。

2、floyd

这个算法复杂度O(n*n*n),所以对于点比较少的时候还行。它的思想是这样的:对于A,B节点之间的路径,而该路径对于k节点无非就是经过与不经过的问题。所以我们定义dis[a,b]为a到b的最短距离,我们可以枚举所有的k节点,在经过或不经过的所有k点的之中比较,得出a到b之间的最短距离。若符合dis[a,b]>dis[a,k]+dis[k,b],那么我们就更新

dis[a,b]=dis[a,k]+dis[k,b];

代码大概如下:

for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)
        for(int j=0;j<n;j++){
            if(Map[i][k]==inf||Map[k][j]==inf) continue;
            if(Map[i][j]>Map[i][k]+Map[k][j]||Map[i][j]==inf){
                Map[i][j]=Map[i][k]+Map[k][j];///通过不断的松弛操作,把节点更新了
            }
        }
这里要注意了,k循环必须放在外面,因为如果放在最内的循环的话,每次更新的时候dis[i,j]就马上确定下来了,这样做法是错误的:


可以看下面一个例子:

如果放在内循环,那么更新得到的dis[A,B]将是9,但正确的是6。放在内循环的话还没来得及更新dis[B,D]这两点就把A,B确定下来了,得到的答案显然是不对的。若放在外循环的话,每一轮更新都会用到前面求得的dis,这有动态规划的思想,直到最后所求的就是所有点两两之间的距离都是最短的。

3、SPFA

在求最短路时如果有负权的边dijkstra等算法没有用武之地,而Bellman-Ford的复杂度有过高,而spfa可高效的解决问题。

spfa的基本是思想是:用一个d[]数组来维护其它点到源点的距离,用一个队列来保存要优化的点,每次从队列里出一个点,并更新其它与该点相连的点到源点的距离,更新的这些点如果在队列中,则不管,如果不在队列中,则将其加入到队列里面。这样不断入队出队,直到队列为空,结果就出来了。这种方法称为动态逼近法。这个算法还可以判断是否存在负环,当每个节点出队的次数超过了n次,那么存在负环。

代码如下:

int spfa(int start){
    queue<int >Q;
    Q.push(start);///首先得队列里的是源点
    low[start]=0;///初识化源点到源点的距离
    vis[start]=true;///表示源点在队列中

    while(!Q.empty()){//cout<<"sdf";
        int temp=Q.front();
        vis[temp]=false;
        outqueue[temp]++;///计算出队次数
        if(outqueue[temp]>n) return false;///判断负环。
        Q.pop();
        for(int v=head[temp];v!=-1;v=E[v].next){
         if(low[E[v].to]>low[temp]+E[v].v){
            low[E[v].to]=low[temp]+E[v].v;///不断松弛

         if(!vis[E[v].to])
            vis[E[v].to]=true;
            Q.push(E[v].to);///若更新的点不再队列,则将其纳入队列
         }
        }
    }

  return low[N];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值