1736 年,普鲁士的哥尼斯堡城里有一条河,河中有两个小岛,河岸与岛屿之间一共架了 7 座桥。市民们热衷于一个游戏:能不能走遍这 7 座桥,而且每座桥恰好只走一次,最后回到出发点?没有人成功过。
数学家欧拉把这个问题抽象成了图:把每块陆地画成一个点,把每座桥画成一条边。于是问题变成:"能不能沿着边一次走完所有边,每条边恰好走一次?" —— 这就是后世所说的欧拉路径问题。
欧拉的观察极其简单却极其深刻:每次"经过"一个点,都要用掉两条边(一进一出)。所以除了起点和终点之外,其余每个点连接出去的边数必须是偶数。而哥尼斯堡的四个陆地上分别连着 3、3、3、5 座桥 —— 四个点全都是奇数,远远超出了"最多只能有两个奇点"的限制,所以这个游戏永远不可能完成。
二百多年后,一位工程师小陈在为校园开放日设计"打卡路线":校园里有若干地点,地点之间有道路,要求走遍每条道路恰好一次。他先算出每个地点的道路条数(度数),发现奇数度的地点恰好有两个 —— 于是他断定"可以从其中一个奇点出发,到另一个奇点结束",然后用 Hierholzer 算法几行代码就把整条路线算了出来。
同一天,教务处要给"学生"和"课程"配对:每个学生选了几门课,每门课有容量上限,要求给尽可能多的"学生—课程"配对。小陈一看就笑了:这是一个二分图 —— 左边全是学生、右边全是课程,同一侧内部没有边。而这种图的判定极其简单:从任意点开始染色,相邻点染不同色,如果染得下去就是二分图,出现同色相邻就不是。
欧拉路径管"一次走完所有边",二分图管"能否把点分成互不相邻的两堆" —— 一个关于边,一个关于点。
💡 故事背后的算法思想
欧拉路径的判定是"度数"的算术:无向图中,每次进入一个点必然要离开它(除了起点和终点),所以"奇度点"的角色就是"起点或终点"。因此奇度点的个数只能是 0 或 2(不能是 1、3、4……)。再加上"所有有边的点必须连通",判定就完成了。
Hierholzer 的核心是"回溯拼接":先随便走一条路,走到走不动为止,这样就找到了一个"回路"(或一条"从起点到终点的链")。如果还有没走过的边,那么这些边一定和当前路径上的某个点相连 —— 从那个点出发再走一个回路,然后"插"进原来的路径里。反复拼接,最终得到完整的欧拉路径。
二分图的判定是"染色":任选一个点染成黑色,它的所有邻点必须染成白色(因为它们与它相邻,不能同色),再往外一层又必须染成黑色……如果某一步发现"应该染黑色的点已经是白色了",说明存在矛盾 —— 而这个矛盾恰好等价于"图中存在奇数长度的环"。