交互式图论学习
交互式图论学习
Guest User
Using app without sign in
哈密顿路径查找器
找到恰好访问每个顶点一次的路径
选择算法并生成步骤以开始可视化
哈密顿路径恰好访问图的每个顶点一次;哈密顿回路还要返回起始顶点。判定这样的路径是否存在是 NP 完全的,与可在线性时间内判定的欧拉路径形成鲜明对比。
精确算法使用回溯:每次将部分路径扩展一个顶点,当当前顶点没有未访问邻居时剪枝。基于子集的动态规划(与 Held-Karp 相同的位掩码技术)在 O(n 的平方乘以 2 的 n 次方) 内求解。有用的剪枝规则包括度数检查和对剩余图的连通性测试。对于特殊图类,如竞赛图或满足 Dirac 或 Ore 度数条件的图,存在性有保证且有构造性算法。
哈密顿路径出现在基因组组装、电路设计与测试、骑士巡游等益智游戏中,也是 TSP 的结构核心。在面试中,位掩码动态规划解法是标准的高难题目,与欧拉路径的对比可考查概念清晰度。
目前没有已知的多项式算法,因此诚实的写法是带剪枝的回溯。正是剪枝让它勉强可用。
哈密顿路径(图):
对每个起点 s:
若 回溯([s], {s}): 返回该路径
返回 无解
回溯(路径, 已访问):
若 已访问 包含全部结点: 返回 真
u = 路径.末端
对 u 的每个未访问邻居 v:
// 值回票价的剪枝:
// - 某个未访问结点此刻已不可达 -> 失败
// - 剩余图中有两个或更多度为 1 的
// 未访问结点 -> 失败
已访问.加入(v)
若 回溯(路径 + [v], 已访问): 返回 真
已访问.移除(v) // 撤销并尝试下一个
返回 假可达性剪枝是其中最要紧的一条。选定一条部分路径之后,对未访问的结点跑一次快速遍历;只要其中任何一个此刻已与当前末端断开,这条分支就已死亡,可以立刻放弃,而不必等到整棵子树都搜完。在稀疏图上,这能把一次不可行的搜索变成一次很快的搜索,尽管最坏情况仍是指数级。
在一个五结点图上从 A 出发寻找哈密顿路径,并看清同一个图为何不存在哈密顿回路。
示例图: 无向边 A-B、B-C、C-D、D-E,外加两条弦 A-C 与 B-D。
存在两条哈密顿路径 A-B-C-D-E 与 A-C-B-D-E,但不存在哈密顿回路。度为 1 的结点 E 几乎独力解决了这两个问题:它把自己钉成路径端点,同时排除了任何回路,因为回路要求每个结点的度至少为 2。在开始搜索之前先检查度数既便宜又往往具有决定性。
时间: 朴素 O(V!),动态规划 O(V^2·2^V) · 空间: 动态规划 O(V·2^V)
朴素回溯实际在枚举排列,最坏情况为 O(V 的阶乘),超过大约 12 个结点就毫无希望。基于子集的 Held-Karp 动态规划要好得多:状态是「已访问结点的子集」加上「当前末端」,共有 2 的 V 次方乘以 V 个状态,每次转移花费 O(V),因此总计为 O(V 的平方乘以 2 的 V 次方) 时间和 O(V 乘以 2 的 V 次方) 内存。这在大约 20 个结点以内是可行的,此时 2 的 20 次方乘以 20 约为两千万个状态。该问题是 NP 完全的,因此不预期存在多项式算法;不过带剪枝的回溯在真实的稀疏图上常常很快就能结束。
在动用指数级手段之前,先确认你面对的究竟是哪个问题,因为其中有两个其实很容易。
| 替代算法 | 以下情况更合适 | 代价 |
|---|---|---|
| 欧拉路径 | 你需要的是每条「边」用一次,而不是每个结点。统计度数即可,线性时间。 | O(V + E) |
| Held-Karp 动态规划 | 结点数少于约 20,且你需要一个确定的是或否。 | O(V^2·2^V) |
| TSP 启发式算法 | 图是带权完全图,而你要的是一条好回路而不是存在性证明。 | 每轮 2-opt 为 O(n^2) |
| Dirac 与 Ore 充分条件 | 你只需要证明回路存在。若每个结点的度都至少为 V/2,那么回路必然存在,完全无需搜索。 | O(V) |
| 拓扑排序 | 图是有向无环图。哈密顿路径存在,当且仅当唯一拓扑序中相邻的结点两两之间有边。 | O(V + E) |