1 简介
先来一道题: 家谱树
如果画出这张图, 会发现它是没有环的, 这也是拓扑排序适用的DAG(有向无环图)
在这道题中, 每个人想要输出, 就要先输出他的父亲, 按照这个规律输出的序列叫做拓扑序列, 可以看出一张图的拓扑序列是不唯一的, 我们可以把入度为
0
0
0的点视作起点
2 分类
拓扑排序可以分成 b f s bfs bfs形式和 d f s dfs dfs形式
2.1 bfs形式
步骤如下:
- 建立一个队列和一个统计入度
- 将入度为 0 0 0的点入队
- 取出队首元素并输出
- 删除此点为起点所有的连边
- 将入度为 0 0 0的点入队
- 如果队列不为空重复 3 − 5 3-5 3−5步
如果输出的点数与图中顶点数相同说明图中所有节点已经被遍历过了, 时间复杂度是
O
(
N
+
E
)
O(N+E)
O(N+E)
如果没有, 说明图中存在环
2.2 dfs形式
步骤如下:
- 建立 v i s vis vis数组统计点是否被遍历到, 一个记录序列的列表 t o p o topo topo
- 遍历所有点, 如果没有遍历过且此点入度为 0 0 0, 从此点开始 d f s dfs dfs
- d f s dfs dfs遍历每一个点可以连接到的点, 如果此点没有被遍历到
- 按照返回的顺序把点加入 t o p o topo topo数组, v i s i = 1 vis_i = 1 visi=1
- 最后倒叙数出 t o p o topo topo数组
3 家谱树AC代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int N = 105;
int n, du[N];
vector<vector<int> > g(N);
priority_queue<int, vector<int>, greater<int> > q;
int main(){
scanf("%d", &n);
for(int i = 1;i <= n;i++){
int x;
while(~scanf("%d", &x) && x != 0){
du[x]++;
g[i].push_back(x);
}
}
for(int i = 1;i <= n;i++)
if(du[i] == 0)
q.push(i);
while(!q.empty()){
int x = q.top();
q.pop();
printf("%d ", x);
for(int i = 0;i < g[x].size();i++){
int y = g[x][i];
du[y]--;
if(du[y] == 0) q.push(y);
}
}
return 0;
}