• 再谈Dijkstra算法和堆优化


    Orz,今天和Java老师讨论到了图的遍历,然后扩展到最短路。感觉现场在黑板写还是有点紧张,大脑一下子有点懵,委屈本身自己说话就有点口吃,好多都没表达出来,也不知道怎么表达。感觉如果以后面试的时候这样面对HR提问肯定会爆炸啊!!!大哭大哭大哭自己汉语都说不流利,还学毛线的外语!哭

    下面再回顾下dij算法思路和代码:


    如上图,从点A->点F,最短路径为A->C->D->F,Min=3+3+3=9

    首先Java老师是我认为讲的最干练的一位老师了,虽然课时有限,我之前搞过一段时间Java,感觉以前遇到的重难点老师都点到了。而其他小枝末节让我们自己下来实践。

    老师一直强调了dfs(深度优先遍历)的重要性,确实,dfs在图论中一直占据着重要的角色。可扩展到图论中割顶,桥,拓扑,双连通分量,强连通分量问题的解决上。基于深搜的Tarjan算法我到现在都还没时间看(Orz,大二下学期每天满课)。在做一些算法题目的过程中,深搜感觉可以解决大部分搜索类题目了,但是深搜难在剪枝,剪枝可以减少大量不必要的搜索过程。这部分我自己做的不够好。投入时间首先都没达到。

    用邻接矩阵的Dijkstra算法的代码:

    int mp[maxn][maxn];
    int dis[maxn];
    bool visit[maxn];
    int n,m;   //V,E
        void Dijkstra( int s )
        {
            int i,v,u;
            for( i=1; i<=n; ++i )
            {
                visit[i]=false;
                dis[i]=mp[1][i];
            }
            dis[s]=0;
        while( true )
        {
            v=-1;
            for( u=1; u<=n; ++u )
                if( !visit[u] && ( v==-1 || dis[u]<dis[v]) )
                    v=u;
            if( v==-1 ) break;
            visit[v]=true;
    
    
            for( u=1; u<=n; u++ )
                dis[u]= min( dis[u],dis[v]+mp[v][u] );
        }
    }
    

    Dij算法是基于广搜,松弛的时候有点贪心和动态规划的思想。

    使用邻接矩阵实现的dijkstra算法的复杂度是O(V²)。使用邻接表的话,更新最短距离只需要访问每条边一次即可,因此这部分的复杂度是O(E).但是每次要枚举所有的顶点来查找下一个使用的顶点,因此最终复杂度还是O(V²)。在|E|比较小时,大部分的时间都花在了查找下一个使用的顶点上,因此需要使用合适的数据结构进行优化。

      优先队列+dijkstra算法:
        总时间复杂度=找最短距离  u := vertex in Q with min dist[u] 的时间复杂度 +
               更新距离   dist[v] := min{dist[v],dist[u] + length(u, v)} 的时间复杂度
        对于一个无向图G(V,E)来说,
          找最短距离的时间复杂度为O(|V|*|V|)(共循环V次,每次V个点),考虑到Q每次递减1,实际复杂度为O(|V|^2/2);
            由于图共有E条边,每条边最多被更新2次(1条边2个端点),因此更新距离的时间复杂度为O(2*|E|)。
          因此,总时间复杂度=O(2*|E|+|V|^2/2)

      然后,实际情况中经常会遇到 |V|^2>>|E| 的稀疏图,即O(2*|E|+|V|^2/2)=O(|V|^2/2)~
          因此,如果我们能够优化 findMIN部分,即可大大优化稀疏图下的dijkstra算法~
          findMIN的部分优化方法很多,最简单的就是用二分搜索O(logN)代替线性搜索 O(N)~
          这里我们将集合Q转化成一个优先队列(priority queue),这样findMIN的时间复杂度变成了O(1),而每次更新priority queue需要花费O(log|V|)~
          综上,采用优先队列之后,总时间复杂度=O(2*|E|+|V|*log|V|),
         这样的优化对于稀疏图(|V|^2>>|E|)来说,尤为有效~

    堆的实现原理这里就不说了,在很多书里面都有详细介绍。


    下面是使用STL的priority_queue实现。在每次更新时往堆里插入当前最短距离和顶点的值对。

    #include <iostream>  
    #include <cstdio>  
    #include <queue>  
    #include <vector>  
    using namespace std;  
    const int Ni = 10000;  
    const int INF = 1<<27;  
    struct node{  
        int x,d;  
        node(){}  
        node(int a,int b){x=a;d=b;}  
        bool operator < (const node & a) const  
        {  
            if(d==a.d) return x<a.x;  
            else return d > a.d;  
        }  
    };  
    vector<node> eg[Ni];  
    int dis[Ni],n;  
    void Dijkstra(int s)  
    {  
        int i;  
        for(i=0;i<=n;i++) dis[i]=INF;  
        dis[s]=0;  
        //用优先队列优化  
        priority_queue<node> q;  
        q.push(node(s,dis[s]));  
        while(!q.empty())  
        {  
            node x=q.top();q.pop();  
            for(i=0;i<eg[x.x].size();i++)  
            {  
                node y=eg[x.x][i];  
                if(dis[y.x]>x.d+y.d)  
                {  
                    dis[y.x]=x.d+y.d;  
                    q.push(node(y.x,dis[y.x]));  
                }  
            }  
        }  
    }  
    int main()  
    {  
        int a,b,d,m;  
        while(scanf("%d%d",&n,&m),n+m)  
        {  
            for(int i=0;i<=n;i++) eg[i].clear();  
            while(m--)  
            {  
                scanf("%d%d%d",&a,&b,&d);  
                eg[a].push_back(node(b,d));  
                eg[b].push_back(node(a,d));  
            }  
            Dijkstra(1);  
            printf("%d
    ",dis[n]);  
        }  
        return 0;  
    }  
    /* 
    6 6 
    1 2 2 
    3 2 4 
    1 4 5 
    2 5 2 
    3 6 3 
    5 6 3 
    */  

    最后一点,dij算法不能解决负权值问题。还是需要使用Bellman-Ford算法或者SPFA算法。

    自己还是菜的要命哭

    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    Linux——配置secureCRT远程连接图形化显示
    Oracle——insert ino,insert all into,insert first into
    Mysql——case函数
    Mysql——语句执行顺序
    Mysql——实现按字段部分升序,部分降序的方法
    无线网ping虚拟机
    http协议
    eclipse——32位64位Eclipse和jdk对应关系
    2019-07-24_windows系统一些常用的dos命令
    2019-07-23_zabbix监控安装视频教程
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/7256606.html
Copyright © 2020-2023  润新知