最短路---Dijkstra+堆优化学习笔记

Dijkstra算法+堆优化


 

Dijkstra算法步骤:

把顶点V分成两组:

S:已经求出最短路径的顶点集合

T=V-S:尚未确定最短路径的顶点集合

1初始时:令S={V0}  T={其余顶点}  T中的顶点对应的距离值若存在<V0,Vi>,则为该边的权值,若不存在则为INF(正无穷) 

2从T中选取一个距离最小的顶点W,将该点加入集合S中。并用该点对T中顶点的距离进行修改:若加入w作为中间顶点(V0-->W-->Vn),该路径的距离比不加入W的路径更短,则修改此距离值。

3重复上述步骤,知道S中包含所有顶点,即S=V为止


 分析:

可知Dijkstra算法的复杂度是O(n*n)

Dijkstra算法在寻找集合T中距离最小的顶点W(即寻找dist数组中最小值复杂度是O(n),在更新距离操作时复杂度为O(n)

上述操作需要进行n次,将n个点的最短路径值确定因此复杂度为On)。可以发现外层的n次循环是不可避免的,因为需要求出源点到各个点的最短路径。可以优化的地方就在寻找dist数组最小值。

可以采用合适的数据结构优化该过程,这里采用了小根堆。小根堆查找最小值时复杂度为O(1),更新里面的值时复杂度为O(logn).最后可将Dijkstra复杂度降至O(nlogn).

这里使用C++  STL中的priority_queue实现小根堆的操作,因为priority_queue默认是大根堆,因此需要重载小于运算符,变成小根堆

 


 

代码实现

这里同样以一道模板题来展示该代码 POJ - 2387 Til the Cows Come Home)

//链式前向星存图+迪杰斯特拉堆优化 
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAX=1005;
const int MAXN=4009;
const int INF=0x3f3f3f3f;
int head[MAX],cnt=0;
int t,n,a,b,len;
int dist[MAX];
bool vis[MAX];
struct Edge{                            //链式前向星 
    int next,val,to;
}Edge[MAXN];
inline void add(int u,int v,int w)
{
    Edge[cnt].to=v;
    Edge[cnt].val=w;
    Edge[cnt].next=head[u];
    head[u]=cnt++;
}
struct node
{
    int pos,dist;                        //点的位置及距离 
    node(){}
    node(int p,int d)
    {
        pos=p;
        dist=d;
    }
    bool operator < (const node &rhs)const        //重载 < 
    {
        return dist>rhs.dist;
    }
};
void Dij(int start)
{
    priority_queue<node>que;
    for(int i=1;i<=n;i++)
    {
        dist[i]=INF;
        vis[i]=false;
    }
    dist[start]=0;
    que.push(node(start,0));
    
    while(!que.empty())
    {
        node temp=que.top();                //优先队列为首的元素及dist数组的最小值 
        que.pop();
        int v=temp.pos;                        //筛选出最小值 
        if(vis[v])continue;                    //判断是否已经找到最小值 ,是的话跳过 
        vis[v]=true;
        
        for(int i=head[v];i!=-1;i=Edge[i].next)        //用最小值的点为弧尾的边更新距离 
        {
            int to=Edge[i].to;
            if(dist[to]>dist[v]+Edge[i].val)
            {
                dist[to]=dist[v]+Edge[i].val;
                que.push(node(to,dist[to]));
            }
        }
    }
}
int main()
{
    while(scanf("%d%d",&t,&n)!=EOF)
    {
        memset(head,-1,sizeof(head));
        for(int i=0;i<t;i++)
        {
            scanf("%d%d%d",&a,&b,&len);
            add(a,b,len);
            add(b,a,len);
        }
        Dij(1);
        printf("%d\n",dist[n]);
    }
    return 0;
}

其他存图方式的Dijkstra算法代码在上一篇博客中介绍到最短路---Dijkstra,因为大致相仿,仅修改了寻找最小值的方式(优先队列优化)不一一贴出。

如有错误和不足的地方,欢迎指正,谢谢~

 

posted @ 2018-08-15 11:41  _Carrot  阅读(2639)  评论(0编辑  收藏  举报