【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)

在软件开发领域,任务指派和数据关联是一种常见业务需求,比如买卖订单的匹配,共享出行的人车匹配,及自动驾驶领域中目标追踪。

这都牵扯到一种技术,那就是数据关联,而匈牙利算法就是解决此类问题最典型的算法,也是今天本文的主题。

我们感性的认为目标之间的匹配好像一目了然的样子,但是计算机可不这样认为。计算机是理性的,如果要处理问题,一般我们会用数据结构来表示数据,栈、队列、树、图都很常用。

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)

不过,这是一种特殊的图,叫做二分图

二分图图最大的特点就是,图中的顶点可以分别划分到两个 Set 当中,每个 Set 中的顶点,相互之间没有边连接。

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)

上图 a 中的红线可以是一个集合,而 b 不是。

最大匹配

按照定义,我们很容易知道,一个图中可以有许多匹配,而包含边数最多的那个匹配,我们称之为最大匹配。

完美匹配

如果一个匹配,它包含了图中所有的顶点,那么它就是完美匹配。

完备匹配

完备匹配的条件没有完美匹配那么严苛,它要求一个匹配中包含二分图中某个集合的全部顶点。比如,X 集合中的顶点都有匹配边。

匈牙利算法就是要找出一个图的最大匹配。

算法思想

其实匈牙利算法如果要感性理解起来是很容易的,核心就在于

冲突和协调

我们看看下图:

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
从顶点 A 开始,E 点可以匹配,那么就直接匹配。并将边 AE 加入到匹配中。
第 2 步
【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
当 C 点开始寻求匹配时,可以发现 CE 和 AE 起冲突了。
前面讲到了冲突和协调两个关键词。
匈牙利算法是让CE 先匹配, AE 取消匹配,然后让 A 尝试重新匹配。

第 4 步

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
B 点寻求匹配的过程,没有再遇到冲突,所以,BH 直接匹配。

需要注意的是,这条路径是从顶点 C 出发的,发生了 2 次冲突,协调了两次,最终协调成功。

如果协调不成功的话,CE 这条路就不成功,它需要走另外的边,但是上图中 C 只和 E 有可能匹配,所以,如果协调不成功,C 将找不到匹配。

第 6 步

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)

代码很简单,详情见注释,打印结果如下:

到这里,就可以了,匈牙利算法的思想和代码编写我们都掌握了。

但是,文章最后,我还是想用更学术的方式去描述和编写这个算法。

用学术的目的是让我们显得更科班,不能总是野路子凭灵感去弄是吧/p>

学术化的目的是为了让我们的思维更严谨,夯实我们的技术基础,这样遇到新的问题时,我们能够泛化解决它。

交替路与增广路

我们现在站在结果处回溯。

上面求得匈牙利的匹配结果如下图:

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
我们可以观察到,匈牙利算法要求得的匹配结果,可以用上图形式表示。

红实线表示两顶点匹配,灰色的虚线表示未匹配。

上面的路径中匹配的边和未匹配的边交替出现。

所以,我们的目标是去构建这样一条路径。

这涉及到两个概念:交替路和增广路。

什么是交替路/h4>

从未匹配的顶点出发,依次经过未匹配的边,匹配的边,未匹配的边,这样的路径就称为交替路。

重点是未匹配的点和未匹配的边开始

什么是增广路/h4>

从未匹配的顶点出发,按照交替路的标准寻找顶点,如果途经了另外一个未匹配的点,那么这条路径就是增广路。

增广路有一些重要的特征:

  1. 增广路中为匹配的边的数量要比匹配的边的数量多 1 条。
  2. 增广路取反(匹配的边变成未匹配,未匹配的边变成匹配)后,匹配的边的数量会加 1,从而达到匹配增广的目的。

而匈牙利算法其实就是就可以看做是一个不断寻找增广路的过程,直到找不到更多的增广路

下面图例说明:

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
首先,从顶点 A 开始,如果要寻求增广路,它应该先走未匹配的边,可以选择 AE,因为 E 点也未匹配,所以 A –> E 符合增广路的定义,它就是一条增广路。

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
再从 C 点寻找增广路,它走未匹配的边,CE,然后走 EA,再走 AF,再走 FB,最后 BH,因为 H 是未匹配的点,所以 C–>E–>A–>F–>B–>H 就是一条增广路,如上图。

【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)
我们再对 D 顶点寻找增广路。
【小算法】二分图匹配之匈牙利算法详解(图例说明,代码亲测可用)

打印结果如下:

也许有同学会问,怎么没有看见对增广路取反/p>

其实,match_list 数组就保存了匹配关系,当改变其中的值顶点的匹配关系就变了,并不需要用一个真正的链表来表示增广路。

文章知识点与官方知识档案匹配,可进一步学习相关知识

来源:frank909

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年9月8日
下一篇 2019年9月8日

相关推荐