理解Floyd-Warshall算法

我们之前分别讨论了Dijkstra算法Bellman-Ford算法,它们解决的都是单源最短路径问题。本文讨论的Floyd-Warshall算法(下称FW算法)则适用于解决可有负权边但不可有负权环的“全局最短路径问题”(all-pairs shortest path problem,或叫做“任意两点间的最短路径问题”)。

为了方便阐述思路,下文的讨论里默认处理的问题还满足下面的特点:

  1. 有权图
  2. 两节点之间最多有一条边
  3. 无自回路(任何节点无指向自身的边)

下图为一个示例:

image

不满足上面三条限制的图需要先进行转化,然后才能用下述的FW算法来解决。其可行性和具体过程,留给读者自行探索。

算法思路

若图G有n个节点(即n = |V|),将这所有节点做任意一个排序,依次记为v_1, v_2, ..., v_n。对于任意的起点u和任意的终点w,从它们之间的路径中挑出所有中间点只含前k个节点(即{v_1, v_2, ..., v_k},其中k < n)的那些,组成一个集合记为P(u, w, k),并且假设我们已经知晓其中最短路径的长度,记为d(u, w, k);那么从u到w的、所有中间点只含前k+1个节点的路径组成的集合可以记为P(u, w, k+1),它们中间最短者的长度可以记为d(u, w, k+1)。可以很容易想明白这么一个关系,P(u, w, k+1)是P(u, w, k)的一个超集,而且属于前者而不属于后者的路径一定经过第k+1个节点,也就是v_(k+1)。如果P(u, w, k+1)中的某条最短路径不经过v_(k+1),那么d(u, w, k+1)就等于d(u, w, k)。如果经过,那么首先可以知道,这条最短路径一定只经过v_(k+1)一次,否则路径中有回路,与最短矛盾;这也意味着,这条路径可以分为两半——从u到v_(k+1)的路径和从v_(k+1)到w的路径,且有:

  1. 它们的中间点都只由前k个节点组成
  2. 前一半在所有从u到v_(k+1)的中间点只含前k个节点的路径中一定是最短的,其长度为d(u, v_(k+1), k)
  3. 后一半在所有从v_(k+1)到w的中间点只含前k个节点的路径中一定最短,其长度为d(v_(k+1), w, k)

那么此时这条最短路径的长度为d(u, v_(k+1), k) + d(v_(k+1), w, k)。如下图所示:

image

综上,可以得到下面这条重要结论:

d(u, w, k+1) = min(d(u, w, k), d(u, v_(k+1), k) + d(v_(k+1), w, k))

要证明上面思路的正确性,我们还得考虑一下基础情况。当k = 0时,对于图中任意的起终点u和w,如果存在从u到w的边,那么记d(u, w, 0) = G[u][w],否则记d(u, w, 0) = ∞。如此一来,算法的循环不变性质可以表述为:

  • 初始为真:在循环之前(k = 0)时,对于任意的u和w,d(u, w, k)确实为从u直接到w的最短路径的长度(不存在时为无穷大)
  • 迭代不变:这在前面已经证明
  • 终止得证:那么在算法终止时,也就是第n次迭代结束后,d(u, w, n)的值为从u到w(中间点可以为所有节点)的最短路径的长度

代码实现

根据上面的思路,我们可以给出如下的代码实现。下面的代码出自Python Algorithms一书,稍有改动,下同。

from copy import deepcopy
from math import inf


def floyd_warshall(G):
    D = deepcopy(G)
    for v in G:
        for u in G:
            for w in G:
                current = D[u].get(w, inf)
                shortcut = D[u].get(v, inf) + D[v].get(w, inf)
                D[u][w] = min(current, shortcut)
    return D

在上面的代码里,图G由G表示:对于G中任意节点v,G[v]本身也是一个dict,如果存在边(v, w),则G[v][w]是该边的权重。因为这里限制了自回路的情况,对任意的节点v,有G[v].get(v, inf)等于inf而非0;这一点请留意。

首先讲讲算法的空间复杂度。在上节的思路分析里,我们用了起点、终点、可用中间点的最大序号三个参数来表示子问题的解,所以很容易以为代码中需要一张三维的表来做记录,而实际上这里相当于只用了一张二维的表,D,它的结构与G完全相同。其实说到这里,应该已经可以明确,Floyd-Warshall算法运用的是动态规划思想。动态规划的关键在于把原问题拆分为互有重叠的子问题。FW算法就是按照可用中间点的最大序号,把原问题分拆成了很多级子问题。而D就是用来存储子问题的解的。之所以不需要三维而只需二维的表,是这个大问题的内在结构决定的:每一级的子问题只依赖于低一级的子问题的解,而不需要更低级别的子问题的解,所以可以覆盖原来的值,故而总体上的空间复杂度为O(|V|2)。

算法的时间复杂度显然为O(|V|3),因为有三层循环。要注意的是循环的顺序:最外层的循环变量一定得是中间点。这是因为子问题是以可用中间点的最大序号来分级的,在解决某一级子问题的时候,需要所有低一级的子问题都已经有解了。

强调一点

从上面的分析来看,FW算法的思想说穿了不难理解,相应地,其代码实现也可以很简单。但其精妙或者说最难想到之处,就是它构建子问题的方法:在缩小了问题规模的同时保证了子问题与原问题有同样的结构。做到这一点,依靠的是引入一个不怎么直观的限制:路径中间点所能选择的集合。

其实这种引入新的限制参数来分拆问题的技巧,尤其在动态规划的算法里,不算少见。例如硬币面额组合问题的解法,就用到了这个技巧。以后在遇到新问题时,如果觉得可以用动态规划来解,但是不知道如何分拆为子问题时,不妨打开下思路,试试看能不能引入新的限制,也许就能豁然开朗。

结语

本文讨论了Floyd-Warshall算法,这里对关键点做个总结:

  • FW算法解决的是全局最短距离问题,适用的图中允许有负权边,但不可有负权环
  • FW算法运用了动态规划的思想,其核心在于限制路径中间点的选择
  • FW算法的时间负责度为O(|V|3),空间复杂度可以达到O(|V|2)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容