拓扑排序(Topological-Sort)是指将有向无环图(DAG)中的节点按照一定的顺序排列的算法。
在拓扑排序中,若存在一条从节点 A 到节点 B 的有向路径,那么在排序后,节点 A 出现在节点 B 的前面。
有向无环图(DAG图)
DAG图(Directed Acyclic Graph)可以把一个图转换为完全线性的结构
AOV网
AOV网(Activity On Vertex Network)是有向无环图。AOV网的排序被称为拓扑排序
在拓扑排序中,一个顶点的前驱是指所有入边的起点,后继是指所有出边的终点。对于一个有向无环图中的每个顶点,都有一个入度表示它的前驱个数和一个出度表示它的后继个数。
实现拓扑排序需要进行以下操作:
1、选择一个入度为 0 的顶点并输出;
2、将该顶点从图中删除,并删除与该顶点相连的所有边;
3、将图中新产生的入度为 0 的顶点加入一个可供选择的顶点集合中。
重复执行上述步骤,直到所有节点均被输出为止。若是存在环,则无法执行拓扑排序。
拓扑排序可以使用 DFS 或 BFS 进行实现:
BFS:
首先将入度为0的顶点入队
while(队列不空){
队头的所有邻接点入度-1
如果存在邻接点入度变为0时则入队
出队
}
出队序列就是拓扑序列
DFS:
每次沿着一条路径一直向下搜索,直到某个顶点的出度为0或被标记已经访问,就停止递归,往回走,在回来的路上记录拓扑排序,即后序遍历
例题
1.家谱树(模版题)
题目描述
有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。 给出每个人的孩子的信息。 输出一个序列,使得每个人的后辈都比那个人后列出。
输入描述
第1行一个整数N(1≤N≤100),表示家族的人数; 接下来N行,第I行描述第I个人的儿子; 每行最后是0表示描述完毕。
输出描述
输出一个序列,使得每个人的后辈都比那个人后列出; 如果有多解输出字典序最小的解。
样例
输入
5
0
4 5 1 0
1 0
5 3 0
3 0
输出
2 4 5 3 1
思路
题目要求有多解则输出字典序最小的解,将队列替换成优先队列,这样,每次从队列中取出的队头都是目前为止度为0权值最小的
代码
#include<bits/stdc++.h>
using namespace std;
int n,a[105],d[105];
vector <vector<int> > v(105);
priority_queue<int,vector<int>,greater<int> > q;
int bfs(){
for(int i=1;i<=n;i++){
if(!d[i]){
q.push(i);
}
}
while(!q.empty()){
int l=q.top();
q.pop();
printf("%d ",l);
for(int i=0;i<v[l].size();i++){
d[v[l][i]]--;
if(!d[v[l][i]]){
q.push(v[l][i]);
}
}
v[l].clear();
}
}
int main(){
scanf("%d",&n);
int x;
for(int i=1;i<=n;i++){
while(scanf("%d",&x)&&x){
d[x]++;
v[i].push_back(x);
}
}
bfs();
return 0;
}
2.是否合法
题目描述
……
题目大意:判断给定的有向图是否存在环
输入描述
输入包含几个测试用例。对于每种情况,第一行包含两个整数,N(要测试的成员)和M(要测试的关系)(2 <= N,M <= 100)。
然后是M行,每行包含一对(x,y)。
输入以N = 0结束。为简单起见,我们给每个人一个数字(0,1,2,…,N-1)。我们使用他们的数字而不是他们的名字。
输出描述
对于每个测试用例,如果无环,则输出“YES”,否则输出“NO”。
样例
输入
3 2
0 1
1 2
2 2
0 1
1 0
0 0
输出
YES
NO
思路
记录拓扑排序遍历到的节点数,与元素总个数进行比较,如果相等,那么无环,如果少于,那么有环。
双层vector的初始化:
for(int i=0;i<100;i++){
v[i].clear();
}
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,d[105];
vector <vector<int> > v(105);
bool bfs(){
priority_queue<int,vector<int>,greater<int> > q;
for(int i=0;i<n;i++){
if(!d[i]){
q.push(i);
}
}
int cnt=0;
while(!q.empty()){
int l=q.top();
q.pop();
cnt++;
for(int i=0;i<v[l].size();i++){
d[v[l][i]]--;
if(!d[v[l][i]]){
q.push(v[l][i]);
}
}
v[l].clear();
}
if(cnt==n){
return 1;
}
else{
return 0;
}
}
int main(){
while(scanf("%d%d",&n,&m)&&n){
int x,y;
for(int i=0;i<105;i++){
v[i].clear();
d[i]=0;
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
v[x].push_back(y);
d[y]++;
}
if(bfs()){
printf("YES\n");
}
else{
printf("NO\n");
}
}
return 0;
}