题目地址:
https://www.acwing.com/problem/content/850/
给定一个 n n n个点 m m m条边的有向图,点的编号是 1 1 1到 n n n,图中可能存在重边和自环。输出其任意一个拓扑序列。如果不存在则返回 − 1 -1 −1。
输入格式:
第一行包含两个整数
n
n
n和
m
m
m。接下来
m
m
m行,每行包含两个整数
x
x
x和
y
y
y,表示存在一条从点
x
x
x到点
y
y
y的有向边
(
x
,
y
)
(x, y)
(x,y)。
输出格式:
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
数据范围:
1
≤
n
,
m
≤
1
0
5
1\le n,m\le 10^5
1≤n,m≤105
可以用BFS来做。开个队列,先将所有入度为 0 0 0的点入队,每次出队一个数,就将其加入答案,并将其所有的出边删掉(即将其邻接点入度减 1 1 1),当其某个邻接点入度为 0 0 0的时候将这个点入队。最后如果答案里的点数小于 n n n说明不存在拓扑序,输出 − 1 -1 −1,否则输出答案。代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int d[N];
bool vis[N];
int res[N], cnt;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool bfs() {
queue<int> q;
for (int i = 1; i <= n; i++)
if (!d[i]) q.push(i);
while (!q.empty()) {
int t = q.front(); q.pop();
res[++cnt] = t;
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
d[j]--;
if (!d[j]) q.push(j);
}
}
return cnt == n;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b;
cin >> a >> b;
add(a, b);
d[b]++;
}
if (!bfs()) puts("-1");
else for (int i = 1; i <= n; i++) cout << res[i] << ' ';
return 0;
}
时空复杂度 O ( n + m ) O(n+m) O(n+m)。
也可以用DFS来做。如果一个图有拓扑序,那么从任意一个顶点开始对其进行DFS,所得的后序遍历的逆序就是一个拓扑序列(准确的说是拓扑序的一部分,因为只有把所有未访问过的顶点都这样做DFS才能得到完整的拓扑序列)。但DFS求拓扑序,要判断是否存在拓扑序,则需要借助一个状态数组,每个顶点有三个状态,未访问和本轮已访问和之前已访问。初始时所有顶点都是未访问,然后从任意一个点开始DFS,当进行这轮DFS的时候,每访问到一个点就标记为本轮已访问,之后访问其邻接点,如果其邻接点里有一个本轮已访问的点,那就有环,说明没有拓扑序;略过之前已访问的点,只继续DFS访问未访问的点。在回溯之前,将当前点标记为之前已访问,之后再也不会访问它(包括以后各轮的DFS也不会访问它),并且将当前点加入答案序列。在若干轮DFS使得图中所有点都变成之前已访问之后,再将答案序列反个序,就得到了拓扑序。代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int vis[N];
int res[N], cnt;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool dfs(int u) {
vis[u] = 0;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (vis[j] == -1 && !dfs(j)) return false;
else if (!vis[j]) return false;
}
vis[u] = 1;
res[++cnt] = u;
return true;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b;
cin >> a >> b;
add(a, b);
}
memset(vis, -1, sizeof vis);
for (int i = 1; i <= n; i++)
if (vis[i] == -1 && !dfs(i)) {
puts("-1");
return 0;
}
for (int i = n; i; i--) cout << res[i] << ' ';
return 0;
}
时空复杂度一样。