图的最短距离(Floyd-Warshall、DijkStra 、Bellman-Ford算法)

屏幕快照 2018-04-26 下午4.11.23.png

/*
Floyd-Warshall 算法:
允许在顶点k中转,可以通过e[a][b] > e[a][k] + e[k][b], 来找到顶点a和顶点b的最短路程,如果顶点k
是从1 — n,那么就找到顶点a到顶点b允许在顶点1—n中转的最短距离。
当将一个顶点k作为中转点,进行完一轮边的松弛操作(e[i][j] > e[i][k] + e[k][j] 则e[i][j] = e[i][k] + e[k][j])后,就会消除顶点k对最短距离的影响~当k从1—n,就相当于逐步消除了顶点1—n对最短距离的 影响,那么此时的结果就是最短距离~
*/

#pragma mark - - Floyd-Warshall 算法
-(void)test1 {
    // 存储边
    int k[9][3] = {{0,0,0},{1,2,2},{1,3,6},{1,4,4},{2,3,3},{3,1,7},{3,4,1},{4,1,5},{4,3,12}};
    
    int n =4; //顶点数
    int m =8; //边数
    int inf = 9999; // 表示无穷大∞
    // e[i][j] 表示i到j的距离为e[i][j]
    int e[10][10];
    
    for (int i=0; i<10; i++) {
        for (int j=0; j<10; j++) {
            if (i==j) {
                e[i][j] = 0;
            }else {
                e[i][j] = inf;
            }
        }
    }
    
    // 赋值
    for (int i=1; i<=m; i++) {
        int u = k[i][0];
        int v = k[i][1];
        e[u][v] =k[i][2];
    }
    
    // Floyd-Warshall 算法核心
    for (int k=1; k<=n; k++) {
        for (int i=1; i<=n; i++) {
            for (int j=1; j<=n; j++) {
                if (e[i][j] > e[i][k] + e[k][j]) {
                    e[i][j] = e[i][k] + e[k][j];
                }
            }
        }
    }
    
    // 打印 数据
    for (int i=1; i<=n; i++) {
        for (int j=1; j<=n; j++) {
            printf("%4d",e[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

/*
Dijkstra 算法:每次找到离源点最近的点,然后以该点为中心扩展,最终得到源点到其余所有点的最短距离
当找到一个离源点最近的顶点v时,dis[v]的值就确定了,因为不可能存在一个顶点k使得dis[v]>dis[k]+e[k][v]
(e[k][v]>0);然后以顶点v为中转点,对边进行松弛操作,即消除了顶点v对最短距离的影响。然后再从其余点中,再
找到一个离源点最近的点,重复上述操作
*/

#pragma mark - -  Dijkstra 算法
-(void)test2 {
    // 存储边
    int k[9][3] = {{0,0,0},{1,2,2},{1,3,6},{1,4,4},{2,3,3},{3,1,7},{3,4,1},{4,1,5},{4,3,12}};
    int inf=9999;
    int n=4; // 顶点数
    int m=8; // 边数
    int e[10][10];
    for (int i=0; i<10; i++) {
        for (int j=0; j<10; j++) {
            if (i==j) {
                e[i][j]=0;
            }else {
                e[i][j]=inf;
            }
        }
    }
    
    // 赋值
    for (int i=1; i<=m; i++) {
        int u = k[i][0];
        int v = k[i][1];
        e[u][v] =k[i][2];
    }
    
    // dis[] 存储离源点的最短路程
    int dis[10];
    for (int i=0; i<10; i++) {
        dis[i] =inf;
    }
    
    // book[] 标记离源点最近的点
    int book[10] ={0};
    // 设置源点
    dis[1] =0;
    
    for (int i=1; i<=n; i++) {
        int min =inf;
        int u=1; //记录离源点最近的点
        // 找离源点最近的顶点
        for (int i=1; i<=n; i++) {
            if (dis[i]<min && book[i]==0) {
                min =dis[I];
                u=I;
            }
        }
        // 标记顶点u
        book[u]=1;
        // 允许在顶点u中转,扩展其他点到源点的距离
        for (int v=1; v<=n; v++) {
            if (dis[v]>dis[u]+e[u][v]) {
                dis[v] = dis[u]+e[u][v];
            }
        }
    }
    
    // 打印 数据
    for (int i=1; i<=n; i++) {
        printf("%4d",dis[I]);
    }
    printf("\n");
    
}

/*
Bemall-Ford 算法:对所有边进行松弛操作,每进行一轮,就相当于扩充了一些新边。当进行了k轮时,相当于
找到了从源点出发“最多经过k条边”到达其余各个顶点的最短距离~最短路径上最多有n-1条边。故需要进行n-1轮

如果进行n-1轮,还可以继续对边松弛成功,那必定存在负权回路

*/

#pragma mark - - Bellman-Ford 算法
-(void)test3 {
    int k[9][3] = {{0,0,0},{1,2,2},{1,3,6},{1,4,4},{2,3,3},{3,1,7},{3,4,1},{4,1,5},{4,3,12}};
    int inf =9999; // 表示无穷大
    int n=4; // 顶点数
    int m=8; // 边数
    
    // u[i] v[i] 分别表示编号为i的边的顶点
    // v[i] 表示编号为i的边的权值
    int u[10];
    int v[10];
    int w[10];
    
    for (int i=1; i<=m; i++) {
        u[i] = k[i][0];
        v[i] = k[i][1];
        w[i] = k[i][2];
    }
    
    //dis[i] 表示顶点i到源点的最短距离
    int dis[10];
    
    for (int i=0; i<10; i++) {
        dis[i] =inf;
    }
    
    // 设置源点1
    dis[1] =0 ;
    
    // 核心代码
    for (int i=1; i<=n-1; i++) {
        int check=0;
        for (int j=1; j<=m; j++) {
            if (dis[v[j]] > dis[u[j]] + w[j]) {
                dis[v[j]] = dis[u[j]] + w[j];
                check =1;
            }
        }
        
        /*
         check=0 表示当前这轮对边的松弛操作 不能使dis[]发生变化,相等于没有扩充新的边,
         那么即使再次循环,也不会松弛成功。故可以提前退出循环
         */
        if (check==0) {
            break ;
        }
    }
    
    // 检查 是否有负权回路
    int flag=0;
    for (int i=1; i<=m; i++) {
        if (dis[v[i]] > dis[u[i]] + w[i]) {
            flag =1;
        }
    }
    
    if (flag==1) {
        printf("存在负权回路 \n");
    }else {
        
        // 打印 数据
        for (int i=1; i<=n; i++) {
            printf("%4d",dis[I]);
        }
        printf("\n");
    }
    
    
}

/*
Bellman-Ford 的队列优化: 每次仅对最短路程发生变化的顶点的所有出边执行松弛操作
当一个顶点k的dis[u[i]]的值确定了,那么由这个顶点直接扩充的dis[v[i]] = dis[u[i]] + w[i],也不会
发生变化,故只需对最短路程发生变化的顶点的所有出边执行松弛操作
对一个顶点的所有出边进行松弛操作,如果存在dis[v[i]] > dis[u[i]] +w[i],则将v[i]点加入队列,
继续上述操作(head++)

Bellman-Ford 算法,我们可以知道 最多执行n*m次松弛操作,就可以获得所有点到源点的最短回路。
*/

#pragma mark - - Bellman-Ford 的 队列优化
-(void)test4 {
    int k[9][3] = {{0,0,0},{1,2,2},{1,3,6},{1,4,4},{2,3,3},{3,1,7},{3,4,1},{4,1,5},{4,3,12}};
    int inf =9999; // 表示无穷大
    int n=4; // 顶点数
    int m=8; // 边数
    
    // u[i] v[i] 分别表示编号为i的边的顶点
    // v[i] 表示编号为i的边的权值
    int u[10];
    int v[10];
    int w[10];
    
    // first[i] 表示顶点i的第一条边的编号
    int first[10];
    for (int i=0; i<10; i++) {
        first[i] = -1;
    }
    // next[i] 表示边i的下一条边的编号
    int next[10];
    for (int i=1; i<=m; i++) {
        u[i] = k[i][0];
        v[i] = k[i][1];
        w[i] = k[i][2];
        
        next[i] = first[u[I]];
        first[u[i]] =I;
    }
    
    //dis[i] 表示顶点i到源点的最短距离
    int dis[10];
    
    for (int i=0; i<10; i++) {
        dis[i] =inf;
    }
    
    // 初始化队列
    int que[100];
    int head =1;
    int tail =1;
    
    // book[]
    int book[10] = {0};
    // 设置源点1
    dis[1] =0 ;
    que[tail] =1;
    tail ++;
    book[1] =1;
    
    // 记录松弛操作的次数
    int sum=0;
    // 标示是否有负权回路
    int flag=0;
    while (head < tail) {
        // 当前顶点
        int t = que[head];
        // 获取顶点k的第一条边
        int k = first[t];
        while (k!=-1) {
            sum ++;
            if (dis[v[k]] > dis[u[k]] + w[k]) {
                dis[v[k]] = dis[u[k]] + w[k];
                if (book[v[k]] == 0) {
                    book[v[k]] =1;
                    que[tail] = v[k];
                    tail ++;
                }
                
            }
            
            // 下一条边
            k=next[k];
        }
        
        // 出队
        /*
         当一个顶点的最短路程变短后,需要对这个顶点的所有边进行松弛操作,但是如果这个顶点的最短路程
         再次变小,需要再次对这个顶点的所有边进行松弛操作~
         */
        book[que[head]] = 0; //把出队的点重新标记为0
        head ++ ; // head++ 才能继续执行队列里的其他点
        
        // 当图不存在负权回路时,最多会进行n*m次松弛操作
        // 如果sum(松弛操作次数)>n*m+1 ,那一定存在负权回路
        // 为什么是n*m,Bellman-Ford 算法最多执行(n-1)*m次就可以使源点到各个顶点的距离达到最小值
        if (sum>n*m+1) {
            flag =1;
            break ;
        }
        
    }
    

    if (flag==1) {
        printf("存在负权回路 \n");
    }else {
        
        // 打印 数据
        for (int i=1; i<=n; i++) {
            printf("%4d",dis[I]);
        }
        printf("\n");
    }
    
}

图片来源于啊哈!算法.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容

  • 参见贪心算法——最短路径Dijkstra算法参见动态规划 目录 0.最短路径问题0.1 最短路径问题描述 0.1....
    王侦阅读 4,808评论 1 9
  • 本文将介绍三种常见的最短路算法:Dijkstra,Floyd,SPFA Dijkstra Dijkstra是有向图...
    maxkibble阅读 1,480评论 0 0
  • 一、定义 在一幅加权有向图中,最短路径是指从顶点s到顶点t的最短路径是所有从s到t的路径中的权重最小者。求解最短路...
    null12阅读 2,463评论 0 4
  • -DFS(Depth First Search):深度优先搜索 访问完一个顶点的所有邻接点之后,会按原路返回,对应...
    Spicy_Crayfish阅读 2,834评论 1 0
  • 芊芊和仔仔的麻麻阅读 162评论 0 0