zoukankan      html  css  js  c++  java
  • 数据结构-图的遍历之Bellman-Ford算法和SPFA算法

    一、Bellman-Ford算法

    1. 用于解决单源最短路径的问题,但也能够处理有负权边的情况。这是与Djikstra算法不同的地方。
    2. 关于复杂度,要比Djikstra的复杂度更高一点。O(VE),而Djikstra复杂度是O(V^2),V是点的数量,E是边的数量
    3. 原理,就是会出现负环的情况,会使得最短路径越来越小,进而产生错误;如果出现负环,源点无法到达,那么也是不会影响求解的。
    4. 设置d数组,用于存储最短路径的距离,如果存在可以到达的负环,那么返回false;如果不存在,那么数组d中存储的就是最短距离,返回true。
    5. 主要思想及伪代码:就是对于图中的边进行V-1轮操作。每一轮遍历所有的边,如果可以更新当前的距离,那么就进行更新;此时如果没有从源点到达的负环,那么数组d就是当前最优值,因此只需再对于所有边进行遍历一次,判断是否还有值可以更新,如果有,那么就说明图中存在源点可以到达的负环,返回false,如果没有那就返回true。
    6. 使用邻接表,如果使用邻接矩阵会使得算法的复杂度达到O(V^3)代码:
    struct node{
        int v, dis;//v为邻接边的目标顶点,dis为邻接边的边权
    };
    vector<node> Adj[MAXN];
    int n;
    int d[MAXN];
    
    bool Bellman_Ford(int s){
        fill(d, d+MAXN; INF);
        d[s] = 0;
        for(int i = 0; i < n-1; i++){
            for(int u = 0; u < n; u++){
                for(int j = 0; j < Adj[u].size(); j++){
                    int v = Adj[u][j].v;
                    int dis = Adj[u][j].dis;
                    if(d[u] + dis < d[v]){
                        d[v] = d[u] + dis;
                    }
                }
            }
        }
        //以下为判断负环的代码
        for(int u = 0; u < n; u++){
            for(int j = 0; j < Adj[u].size(); j++){
                int v = Adj[u][j].v;
                int dis = Adj[u][j].dis;
                if(d[u] + dis < d[v]){
                    return false;
                }
            }
        }
        return true;
    }
    
    • 需要注意点:统计最短路径条数的做法,由于Bellman-Ford算法期间会多次访问曾经访问过的结点,如果还是按照之前的写法,会统计已经出现过的结点,为了解决这个问题,使用记录 前驱结点的数组为set pre[MAXN],当遇见一条和已有最短路径长度相同路径时,必须重新计算最短路径的条数。

    二、SPFA(Shortest Path Faster Algorithm)算法

    1. 这是对Bellman-Ford优化后产生的算法
    2. 我们知道每轮都需要操作每条边,其中存在很多无意义的边,所以我们知道只有当某个顶点的d[u]值发生改变的时候,从它出发的邻接点v的d[v]值才会发生改变
    3. 由此,可以进行优化,建立一个队列,每次将队首顶点u取出,然后对从u出发的所有边u->v进行松弛操作,如果没有获得最优的值就不管,如果获得最优值,v如果不在队列中,那么就添加进入队列。这样操作直到队列中没有结点(说明图中没有从源点可达的负环), 或是某个顶点的入队次数超过V-1(说明图中存在从源点可达的负环)。
    4. 这种优化后的算法期望时间复杂度是O(kE),k是一个图的常数,E是图的边数。但是如果图中存在源点可以到达的负环,那么传统的SPFA的时间复杂度退化成O(VE)。
    5. 如果事先知道图中不会有环,那么num数组的部分可以去掉。
    6. 如果负环从源点不可达,则需要添加辅助顶点C,并添加一条从源点到达C的有向边以及V-1条从C到达除源点外各顶点的有向边才能判断负环是否存在。思考为啥
    • 下面给出SPFA的代码:
    struct node{
        int v, dis;//v为邻接边的目标顶点,dis为邻接边的边权
    };
    vector<node> Adj[MAXN];
    int n, d[MAXN], num[MAXN];//num数组为记录顶点入队列次数
    bool inq[MAXV];//顶点是否在队列中
    
    bool SPFA(int s){
        //初始化
        memset(inq, false, sizeof(inq));
        memset(num, 0, sizeof(num));
        fill(d, d+MAXN, INF);
        //源点入队列部分
        queue<int> Q;
        Q.push(s);
        inq[s] = true;
        num[s]++;
        d[s] = 0;
        //主体部分
        while(!Q.empty()){
            int u = Q.front();
            Q.pop();
            inq[u] = false;
            //遍历u所有邻接边v
            for(int j = 0; j < Adj[u].size(); j++){
                int v = Adj[u][j].v;
                int dis = Adj[u][j].dis;
                //松弛操作
                if(d[u] + dis < d[v]){
                    d[v] = d[u] + dis;
                    if(!inq[v]){
                        Q.push(v);
                        inq[v] = true;
                        num[v]++;
                        if(num[v] >= n) return false;
                    }
                }
            }
        }
        return true;
    }
    

    三、Floyd算法(弗洛伊德算法)

    1. 用来解决全源最短路径的问题,即给定图G(V, E), 求任意两点之间的最短路径,时间复杂度是O(n^3),由于复杂度的限制,n只能限制在200一内,英雌使用邻接矩阵来实现Floyd算法是十分合适的
    2. Floyd算法基于这样一个事实:如果存在顶点k,使得作为中介点时顶点i和顶点j的当前中介点,即当dis[i][k] + dis[k][j] < dis[i][j]时,令dis[i][j] = dis[i][k] + dis[k][j](其中dis[i][j] 表示顶点i到顶点j的最短距离)
    3. 代码:
    void Floyd(){
        for(int k = 0; k < n; k++){
            for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    if(dis[i][k] != INF && dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j]){
                        dis[i][j] = dis[i][k] + dis[k][j];
                    }
                }
            }
        }
    }
    
    作者:睿晞
    身处这个阶段的时候,一定要好好珍惜,这是我们唯一能做的,求学,钻研,为人,处事,交友……无一不是如此。
    劝君莫惜金缕衣,劝君惜取少年时。花开堪折直须折,莫待无花空折枝。
    曾有一个业界大牛说过这样一段话,送给大家:   “华人在计算机视觉领域的研究水平越来越高,这是非常振奋人心的事。我们中国错过了工业革命,错过了电气革命,信息革命也只是跟随状态。但人工智能的革命,我们跟世界上的领先国家是并肩往前跑的。能身处这个时代浪潮之中,做一番伟大的事业,经常激动的夜不能寐。”
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    jsp 认知(2)
    jsp 认知
    Java 匿名类
    Usage of readonly and const
    Javascript 原型继承(续)—从函数到构造器的角色转换
    JavaScript 原型继承开端
    JS 函数调用
    Javascript Basic Operation Extraction
    JS单词形式的运算符
    git问题收集
  • 原文地址:https://www.cnblogs.com/tsruixi/p/12400612.html
Copyright © 2011-2022 走看看