最短路: SPFA算法

SPFA
是Bellman-Ford的队列优化,时效性相对好,时间复杂度O(kE)。
与Bellman-ford算法类似,SPFA算法采用一系列的松弛操作以得到从某一个节点出发到达图中其它所有节点的最短路径。所不同的是,SPFA算法通过维护一个队列,使得一个节点的当前最短路径被更新之后没有必要立刻去更新其他的节点,从而大大减少了重复的操作次数。
SPFA算法可以用于存在负数边权的图,这与dijkstra算法是不同的。
与Dijkstra算法与Bellman-ford算法都不同,SPFA的算法时间效率是不稳定的,即它对于不同的图所需要的时间有很大的差别。
在最好情形下,每一个节点都只入队一次,则算法实际上变为广度优先遍历,其时间复杂度仅为O(E)。另一方面,存在这样的例子,使得每一个节点都被入队(V-1)次,此时算法退化为Bellman-ford算法,其时间复杂度为O(VE)。
SPFA算法在负边权图上可以完全取代Bellman-ford算法,另外在稀疏图中也表现良好。但是在非负边权图中,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法,以及它的使用堆优化的版本。通常的SPFA
http://blog.chinaunix.net/uid-26425645-id-3291898.html

很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。SPFA的复杂度大约是O(kE),k是每个点的平均进队次数(一般的,k是一个常数,在稀疏图中小于2)。

实现方法:建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。

此外,SPFA算法还可以判断图中是否有负权环,即一个点入队次数超过N。
http://www.cnblogs.com/shadowland/p/5870640.html

具体用邻接表怎么存储,看关于 BELLMAN算法的介绍。

#include<stdio.h> 
#include<malloc.h>

struct node{
    int flag;
    int wight;
    struct node* p;
};

struct shuzhu{
    int d;
    int f;
    struct node* p;
};

void INITIALIZE_SOURCE(struct shuzhu*a,int n);
void SPFA(struct shuzhu* a,int n);

int main()
{
    int n=0,m=0;
    int u=0,v=0,l=0;
    int i=0,j=0;
    struct node* pp=0;
    scanf("%d%d",&n,&m);
    struct shuzhu a[n+1];

    //初始化 邻接表 
    INITIALIZE_SOURCE(a,n);

    //存储输入数据到链接表中 
    for(i=0;i<m;i++)
    {
        scanf("%d%d%d",&u,&v,&l);
        pp=(struct node*)malloc(sizeof(struct node)*1);
        pp->flag=v;
        pp->wight=l;
        pp->p=a[u].p;
        a[u].p=pp;
    }
    /* 
    //检验存储代码的正确性 
    for(i=1;i<=m;i++)
    {
        printf("[%d %d]  ",a[i].d,a[i].f);
        pp=a[i].p;
        for(;pp!=NULL;)
        {
            printf("[%d %d]  ",pp->flag,pp->wight);
            pp=pp->p;
        }

        printf("\n");
    }
    */ 

    //SPFA算法,计算 1号点到其他点的最短路 
    SPFA(a,n);

    //打印结果
    for(i=2;i<=n;i++)
    {
        printf("%d\n",a[i].d);
    }

    return 0;
}

void INITIALIZE_SOURCE(struct shuzhu* a,int n)
{
    int i=1;

    a[1].d=0;
    a[1].f=0;
    a[1].p=NULL;

    for(i=2;i<=n;i++)
    {
        a[i].d=10000000;
        a[i].f=-1;
        a[i].p=NULL;
    }

    return;
}

void SPFA(struct shuzhu* a,int n)
{
    int i=-1;
    int j=0;
    struct node* q=0;
    int Q[200000]={0};

    Q[0]=1;
    j=j+1;
    while(i+1 != j)//队列为空
    {
        q=a[Q[i+1]].p;//Q出一个数 ,出队列
        i=i+1;
        while(q!=NULL)//遍历a[Q[i]]那一条链表
        {
            if(a[q->flag].d > a[Q[i]].d + q->wight)//要继续松弛操作,更新数据
            {
                a[q->flag].f=j;
                a[q->flag].d=a[Q[i]].d+q->wight;
                Q[j]=q->flag;//Q进入一个数(刚刚更新的点),入队列 
                j=j+1;
            }

            q=q->p;
        }
    }



    return;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值