Appearance
サイクル検出(Cycle Detection)
グラフに閉路が存在するか判定し、必要に応じてサイクルを復元する。
特徴
| 項目 | 内容 |
|---|---|
| 用途 | 閉路の存在判定・復元 |
| 対象 | 有向・無向グラフ |
| 計算量 | O(V+E) |
| 空間計算量 | O(V+E) |
考え方
DFSで探索中の頂点に戻る辺があれば、サイクルが存在する。
有向グラフ
頂点の訪問状態を3種類に分ける。
0:未訪問1:探索中2:探索完了
state[nv] == 1 ならサイクルが存在する。
単に訪問済みかどうかだけでは判定できない点に注意。
無向グラフ
親へ戻る逆辺を除外し、それ以外の探索中の頂点へ戻る辺があればサイクルが存在する。
多重辺がある場合は、親頂点ではなく親辺のIDで除外する。
実装
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> g(n);
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
}
vector<int> state(n), parent(n, -1), cycle;
auto dfs = [&](auto&& self, int v) -> bool {
state[v] = 1;
for (int nv : g[v]) {
if (state[nv] == 0) {
parent[nv] = v;
if (self(self, nv)) return true;
} else if (state[nv] == 1) {
cycle.push_back(nv);
for (int u = v; u != nv; u = parent[u]) cycle.push_back(u);
cycle.push_back(nv);
reverse(cycle.begin(), cycle.end());
return true;
}
}
state[v] = 2;
return false;
};
for (int v = 0; v < n; v++) {
if (state[v] == 0 && dfs(dfs, v)) break;
}
if (cycle.empty()) cout << "No\n";
else {
cout << "Yes\n";
for (int v : cycle) cout << v << ' ';
cout << '\n';
}
}cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<pair<int, int>>> g(n);
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back({b, i});
g[b].push_back({a, i});
}
vector<int> state(n), parent(n, -1), cycle;
auto dfs = [&](auto&& self, int v, int pe) -> bool {
state[v] = 1;
for (auto [nv, id] : g[v]) {
if (id == pe) continue;
if (state[nv] == 0) {
parent[nv] = v;
if (self(self, nv, id)) return true;
} else if (state[nv] == 1) {
cycle.push_back(nv);
for (int u = v; u != nv; u = parent[u]) cycle.push_back(u);
cycle.push_back(nv);
reverse(cycle.begin(), cycle.end());
return true;
}
}
state[v] = 2;
return false;
};
for (int v = 0; v < n; v++) {
if (state[v] == 0 && dfs(dfs, v, -1)) break;
}
if (cycle.empty()) cout << "No\n";
else {
cout << "Yes\n";
for (int v : cycle) cout << v << ' ';
cout << '\n';
}
}使用例
有向グラフの入力(0-indexed)
text
5 5
0 1
1 2
2 0
2 3
3 4出力
text
Yes
0 1 2 0サイクルが存在しない場合は No を出力する。
注意点
- 有向グラフでは探索中と探索完了を区別する。
- 無向グラフでは親へ戻る辺を除外する。
- 無向多重グラフでは平行辺2本もサイクルを構成する。
- 自己ループもサイクルとして検出できる。
- 非連結グラフにも対応するため、すべての未訪問頂点からDFSする。
- 復元されるサイクルは必ずしも最短ではない。
- 再帰が深くなる場合はスタックオーバーフローに注意する。
別の検出方法
| 対象 | 方法 |
|---|---|
| 有向グラフ | DFS・トポロジカルソート |
| 無向グラフ | DFS・Union Find |
| Functional Graph | 訪問状態の管理 |
| 有向グラフの強連結成分 | SCC |
有向グラフでは、トポロジカルソートで全頂点を列挙できなければ閉路が存在する。
無向グラフでは、Union Findで同じ連結成分に属する2頂点を辺で結ぼうとすると閉路ができる。