📅  最后修改于: 2023-12-03 15:41:49.856000             🧑  作者: Mango
有一张 $n$ 个点 $m$ 条边的有向图,请你判断这张图是否存在环。
第一行两个整数 $n$ 和 $m$。
接下来 $m$ 行,每行两个整数 $a$ 和 $b$,表示存在一条 $a$ 到 $b$ 的有向边。
如果存在环则输出 Yes,否则输出 No。
$1\leq n,m\leq 10^5$
输入:
3 3
1 2
2 3
3 1
输出:
Yes
可以使用深度优先搜索(DFS)或拓扑排序解决该问题。
利用DFS探索每个节点,若当前节点已经访问过,则说明存在环。
bool dfs(int u) {
if (st[u]) return true; // 如果已经访问过则说明存在环
st[u] = true;
for (int i = h[u]; i != -1; i = e[i].ne) {
int j = e[i].j;
if (dfs(j)) return true;
}
st[u] = false; // 回溯
return false;
}
bool check_circle() { // 返回是否有环
memset(st, false, sizeof st);
for (int i = 1; i <= n; i++)
if (dfs(i)) return true;
return false;
}
采用拓扑排序,如果存在环,则必定有点的入度为0,但是遍历完毕后入度为0的点数量不足 $n$ 个,说明存在环。
bool topsort() { // 判断图是否存在环
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++)
if (!din[i]) q[++tt] = i;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; i != -1; i = e[i].ne) {
int j = e[i].j;
if (--din[j] == 0) q[++tt] = j;
}
}
return tt + 1 < n;
}
bool check_circle() { // 返回是否有环
memset(h, -1, sizeof h);
memset(din, 0, sizeof din);
int cnt = 0;
for (int i = 0; i < m; i++) {
int a = edges[i].a, b = edges[i].b;
add(a, b);
din[b]++;
}
return topsort();
}
最后,我们在主函数中调用上述方法即可。
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) cin >> edges[i].a >> edges[i].b;
if (check_circle()) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
用 BFS 的拓扑排序时间复杂度是 $O(n+m)$,DFS 的时间复杂度是 $O(n^2)$,因为最坏情况下每个点都被遍历了一次,每次遍历时需要 $O(n)$ 的时间;空间复杂度均为 $O(n+m)$。