Appearance
欧拉迹:经过图中每条边恰好一次的迹,起点和终点可以相同,也可以不同。
欧拉开路:起点和终点不同的欧拉迹。本文后续所说的“欧拉路径”专指欧拉开路。
欧拉回路:经过图中每条边恰好一次的回路(起点和终点相同)。
注:
- 欧拉路径和欧拉回路对不限制点的经过次数,可重复经过一个点。
- 孤立点不经过任何边,不影响欧拉路径/回路是否存在,无需从图中删除。只需在连通性判定时忽略零度点;当
时,构造起点必须选择非零度点。 - 当图中没有边时,是否把单个顶点上的长度为
的迹视为欧拉回路,应按题目约定处理。
判定:
- 有向图
- 欧拉路径:图中恰好存在
个点出度比入度多 (该点为起点),恰好存在 个点入度比出度多 (该点为终点),其余点入度等于出度。 - 欧拉回路:所有点的入度等于出度;当
时,可以任选一个非零度点作为起点。
- 欧拉路径:图中恰好存在
- 无向图
- 欧拉路径:图中恰好有两个点的度数为奇数(这两个点为起点和终点,起点和终点可互换),其余点的度数为偶数。
- 欧拉回路:所有点的度数都为偶数;当
时,可以任选一个非零度点作为起点。
上述度数条件还必须配合连通性条件:无向图中所有非零度点必须位于同一个连通块;有向图中所有入度或出度非零的点,必须在忽略边方向后位于同一个连通块。孤立点可以属于其他连通块,不影响结论。
若题目把“欧拉路径”用作允许起终点相同的广义称呼,则满足对应的欧拉开路条件或欧拉回路条件之一即可。
求解:
有向图欧拉路径(开路):
根据判定条件,找到唯一的那一个出度
有向图欧拉回路:
根据判定条件,当
无向图欧拉路径(开路):
根据判定条件,找到度数为奇数的一个点,作为起点,dfs 递归第一次经过某条边,递归前删除这条边。
无向图欧拉回路:
根据判定条件,当
注:无向图需要注意标记把同一条边的反边也给删了。
注:上述四种情况的算法实现流程完全一致。
时间复杂度:
Hierholzer 算法在回溯时后序写入答案,因此构造完成后必须反转。即使度数条件已经满足,也应检查是否使用了全部 del,无向图还需清空 vis;包装函数会清空 ans。
有向图:
点击展开代码
cpp
void dfs(int x) {
for (unsigned i = del[x]; i < p[x].size(); i = del[x]) {
del[x]++;
dfs(p[x][i]);
}
ans.push_back(x);
}
bool euler(int st) {
ans.clear();
dfs(st);
reverse(ans.begin(), ans.end());
return (int)ans.size() == m + 1;
}无向图:
点击展开代码
cpp
void dfs(int x) {
for (unsigned i = del[x]; i < p[x].size(); i = del[x]) {
del[x]++;
auto [u, id] = p[x][i];
if (vis[abs(id)])
continue;
vis[abs(id)] = 1;
dfs(u);
ans.push_back(id);
}
}
bool euler(int st) {
ans.clear();
dfs(st);
reverse(ans.begin(), ans.end());
return (int)ans.size() == m;
}递归实现的调用深度最坏可达 id 和 -id,其中
有向图迭代实现:
点击展开代码
cpp
bool euler(int st) {
vector<int> stk;
ans.clear();
stk.push_back(st);
while (!stk.empty()) {
int x = stk.back();
if (del[x] < p[x].size()) {
stk.push_back(p[x][del[x]++]);
} else {
ans.push_back(x);
stk.pop_back();
}
}
reverse(ans.begin(), ans.end());
return (int)ans.size() == m + 1;
}无向图迭代实现:
点击展开代码
cpp
bool euler(int st) {
vector<pair<int, int>> stk;
ans.clear();
stk.push_back({st, 0});
while (!stk.empty()) {
int x = stk.back().first;
while (del[x] < p[x].size() && vis[abs(p[x][del[x]].second)])
del[x]++;
if (del[x] == p[x].size()) {
int id = stk.back().second;
stk.pop_back();
if (id)
ans.push_back(id);
} else {
auto [u, id] = p[x][del[x]++];
vis[abs(id)] = 1;
stk.push_back({u, id});
}
}
reverse(ans.begin(), ans.end());
return (int)ans.size() == m;
}