• 最短路---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
    ",dist[n]);
        }
        return 0;
    }

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

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

  • 相关阅读:
    term "JavaScript"
    Pro Git
    Pro Git
    Pro Git
    git
    flask
    OJ
    [蓝桥杯]Huffuman树
    priority_queue优先队列
    [蓝桥杯]高精度加法
  • 原文地址:https://www.cnblogs.com/LjwCarrot/p/9476140.html
Copyright © 2020-2023  润新知