证明Hierholzer算法求字典序最小的欧拉回路的正确性
看了网上有关此算法的博客,感觉还不太明白的 OI 友可以看此篇文章
为什么写这篇文章
Hierholzer 应该算是一个基础的算法(毕竟它的板子题只有绿)。可能因为这个原因,网上关于它的博客都比较肤浅,尤其是它用于求字典序最小的欧拉回路的正确性。所以本蒟蒻想给它一个完整的证明(可能以下内容对身为大佬的你来说是显然的事情)。
对 Hierholzer 算法过程的分析
这个算法本质是 CircleClear 与 CircleMerge 两个过程的结合
这是 oiwiki 上的代码片段(显然这个算法是以 dfs 为基础的):
void Hierholzer(int x) {
for (int& i = cnt[x]; i < (int)beg[x].size();) {
if (beg[x][i].exists) { // ......*
edge e = beg[x][i];
beg[x][i].exists = beg[e.to][e.revref].exists = false;
++i;
Hierholzer(e.to);
} else {
++i;
}
}
ans.push(x);
}
断言:同一层的函数执行中,打 * 号的分支最多只会进入
介绍 CircleClear
考虑这样一个过程:选定一个起点
简证(反证法):设最后停留点为
也就是说,在上述过程结束后,删掉的边构成了包含
介绍 CircleMerge
对于两条回路
CircleClear + CircleMerge = Hierholzer
当我们选定一个起点
在函数退出是将点加入答案,最后将答案序列翻转,本质上是进行
若要求出字典序最小的欧拉回路,便要在 dfs 的过程中每次优先访问最小的点,正确性证明如下:
证明过程
考虑归纳证明,设该算法能在点数
算法会从字典序最小的位置开始,每次遍历字典序最小的节点,找到一条字典序最小的回路(CircleClear)。
若所有边已被遍历到,则它显然一定是字典序最小的欧拉回路。
否则,去掉这些被遍历的边,图必将分裂成若干联通子图,每个联通子图都是一个欧拉图。对于每一个联通子图,都必须将其一个欧拉回路插入到一开始找到的环中(CircleMerge)。
记找到的环上节点按遍历顺序依次为
对于每一个联通子图的插入,我们的首要任务是使它插入的位置
在 Hierholzer 算法的回溯过程中,一遇到未访问的边就去遍历它,保证了这一点(因为回溯是从
由归纳假设,
综上所述,该算法正确性得证。