检测到 KaTeX 加载失败,可能会导致文中的数学公式无法正常渲染。
本文中提到的所有图均为连通图。
定义
- 通过图中所有边恰好一次的通路称为欧拉路径。
- 通过图中所有边恰好一次的回路称为欧拉回路。
- 具有欧拉回路的无向图或有向图称为欧拉图。
- 具有欧拉通路但不具有欧拉回路的无向图或有向图称为半欧拉图。
判定
有向图
- 存在欧拉路径的充分必要条件:
- 所有点的出度均等于入度;或
- 存在一个点的出度比入度多一(此时该点为起点),存在另一个点的入度比出度多一(此时该点为终点),其余点的入度和出度均相等。
- 存在欧拉回路的充分必要条件:所有点的入度均等于出度。
无向图
- 存在欧拉路径的充分必要条件:度数为奇数的点只能有 个或 个。
- 存在欧拉回路的充分必要条件:不能有度数为奇数的点。
求欧拉回路
从一个非孤立点开始 DFS,点可以重复经过,每次任意走一条边并将这条边从邻接表中删除(如果是无向图,其反向边也要被标记为删除),最终所有边一定会都会被经过。
DFS 回溯时记录每一条边,最终将记录的逆序即为欧拉回路。
代码
对应题目 UOJ #117. 欧拉回路。
#include <iostream>
#include <cstring>
using std::cin;
using std::cout;
const char endl = '\n';
const int N = 1e5 + 5,
M = 2e5 + 5;
int n, m, cnt, ans[M];
int idx, head[N], edge[M], next[M];
int din[N], dout[N];
bool vis[M];
void add(int u, int v) {
next[idx] = head[u];
edge[idx] = v;
head[u] = idx++;
}
void dfs(int u) {
for (int &i = head[u]; ~i;) {
if (vis[i]) {
i = next[i];
continue;
}
int v = edge[i],
x = i + 1;
vis[i] = true;
i = next[i];
dfs(v);
ans[++cnt] = x;
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
memset(head, 0xff, sizeof(head));
for (int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
add(u, v);
dout[u]++, din[v]++;
}
for (int i = 1; i <= n; i++) {
if (din[i] != dout[i]) {
cout << "NO" << endl;
exit(0);
}
}
for (int i = 1; i <= n; i++) {
if (~head[i]) {
dfs(i);
break;
}
}
if (cnt < m) { // 没有经过所有边
cout << "NO" << endl;
exit(0);
}
cout << "YES" << endl;
for (int i = cnt; i; i--) {
cout << ans[i] << ' ';
}
cout << endl;
return 0;
}
#include <iostream>
#include <cstring>
using std::cin;
using std::cout;
const char endl = '\n';
const int N = 1e5 + 5,
M = 2e5 + 5;
int n, m, cnt, ans[M];
int idx, head[N], edge[M << 1], next[M << 1];
int deg[N];
bool vis[M << 1];
void add(int u, int v) {
next[idx] = head[u];
edge[idx] = v;
head[u] = idx++;
}
void dfs(int u) {
for (int &i = head[u]; ~i;) {
if (vis[i]) {
i = next[i];
continue;
}
int v = edge[i],
x = i & 1 ? -(i >> 1) - 1 : (i >> 1) + 1;
vis[i] = true;
vis[i ^ 1] = true;
i = next[i];
dfs(v);
ans[++cnt] = x;
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
memset(head, 0xff, sizeof(head));
for (int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
add(u, v);
add(v, u);
deg[u]++, deg[v]++;
}
for (int i = 1; i <= n; i++) {
if (deg[i] & 1) {
cout << "NO" << endl;
exit(0);
}
}
for (int i = 1; i <= n; i++) {
if (~head[i]) {
dfs(i);
break;
}
}
if (cnt < m) { // 没有经过所有边
cout << "NO" << endl;
exit(0);
}
cout << "YES" << endl;
for (int i = cnt; i; i--) {
cout << ans[i] << ' ';
}
cout << endl;
return 0;
}
参考资料
- 3.10 欧拉回路和欧拉路径,AcWing 算法提高课,闫学灿,2019 年 12 月 7 日。
- 欧拉回路学习笔记,黄浩睿,2017 年 1 月 1 日。