算法专题——欧拉回路
概念:
-
欧拉回路: 一笔画, 起点等于终点.
-
欧拉路径: 一笔画, 起点可以不等于终点.(条件更加宽松).
-
欧拉图: 存在欧拉回路的图.
-
半欧拉图: 仅存在欧拉路径的图.
找欧拉回路
存在的充要条件
A.判断欧拉通路是否存在的方法
有向图:图连通, 有一个顶点出度大入度1, 有一个顶点入度大出度1, 其余都是出度=入度.
无向图:图连通, 只有两个顶点是奇数度, 其余都是偶数度的.
B.判断欧拉回路是否存在的方法
有向图:图连通, 所有的顶点出度=入度.
无向图:图连通, 所有顶点都是偶数度.
特殊情况:孤立点, 孤立点没有边相连, 没有影响, 求解欧拉回路的时候可以直接删掉.
下面进行简单的证明:
起点终点为奇数度的, 先从起点到终点连任意一条边, 可以得到一个皆为偶数度的图. 一个皆为偶数度的图的必定不可能是一棵树, 必定存在一个简单环. 得到一个简单环之后, 将该环从图中删去, 仍然可以得到一个皆为偶数度的图, 如此反复, 最终可以将原图分解为由许多个环组成的图.
可以发现各个环之间不存在公共边, 且一定可以通过某些点进行连接, 因此, 我们总是可以找到一条欧拉路径去遍历所有的边.
DFS
DFS天然具有拓扑的特性, 可以得到对一个欧拉图进行DFS, 回溯的时候将边弹入栈中, 就可以天然得到一个欧拉路径. 看下面代码.
欧拉回路为了保证时间复杂度的性能, 需要将遍历过的边进行删除, 为了使后续访问的删点过程可以影响前面的遍历, 需要用到引用.
代码:
//记录边的路径
void Euler(int u) {
for (int &i = h[u]; ~i; i = ne[i]) {//改为引用
int t = i / 2 + 1;
if (used[t]) continue; // coloring
used[t] = true;
if (i & 1) t = -t;
Euler(e[i]);
ans[++tot] = t;
if (i == -1) break; //prevent ne[-1]
int t = i + 1;
if (used[t]) continue; // coloring
used[t] = true;
Euler(e[i]);
ans[++tot] = t;
if (i == -1) break; //prevent ne[-1]
}
}
void Euler(int u) {
for (int &i = h[u]; ~i; i = ne[i]) {
int t = i / 2 + 1, v = e[i]; //undir
int t = i + 1, v = e[i]; //dir
if (used[t]) continue;
used[t] = true;
Euler(v);
ans[++tot] = v;
if (i == -1) break;
}
}