拓扑排序(Topological Sorting)
拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。DAG的判定
拓扑排序的时间复杂度为O ( V + E ),因为DFS的时间复杂度度为O ( V + E )
该序列必须满足下面两个条件:
- 每个顶点出现且只出现一次。
-
若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。
例如,下面这个图:
它是一个 DAG 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:
- 1.从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
- 2.从图中删除该顶点和所有以它为起点的有向边。
重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
于是,得到拓扑排序后的结果是 { 1, 2, 4, 3, 5 }。
通常,一个有向无环图可以有一个或多个拓扑排序序列。
#include <cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int MAXN=100010;
vector<int> graph[MAXN];
int in[MAXN];
void topoSort(int n)
{
vector<int> zero;//0入度点
vector<int> result;//排序结果
for(int i=1;i<=n;i++)
{
if(in[i]==0) zero.push_back(i);
}
while(!zero.empty())
{
int curr=zero.back();
zero.pop_back();
result.push_back(curr);
int len=graph[curr].size();
for(int i=0;i<len;i++)
{
int v=graph[curr][i];
in[v]--;
if(in[v]==0) zero.push_back(v);
}
}
if(result.size()!=n)
{
printf("Not DAG, fail\n");
return;
}
for(int i=0;i<result.size();i++)
{
printf("%d\n",result[i]);
}
}
int main()
{
int n,m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(graph,0,sizeof(graph));
memset(in,0,sizeof(in));
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
graph[a].push_back(b);
in[b]++;
}
topoSort(n);
}
}
还有一种直观的方法是利用DFS,容易知道拓扑排序的序列为所有顶点的逆后续排列(ReversePostOrder)
#include <cstdio>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
const int MAXN=100010;
vector<int> graph[MAXN];
stack<int> result;//排序结果
int vis[MAXN];
bool DFS(int u)
{
if(vis[u]==2) return true;
vis[u]=2;
int len=graph[u].size();
for(int i=0;i<len;i++)
{
int v=graph[u][i];
if(vis[v]==0)
{
if(DFS(u)) return true;
}
else if(vis[v]==2) return true;
}
vis[u]=1;
result.push(u);
return false;
}
void topoSort(int n)
{
memset(vis,0,sizeof(vis));
bool flag=false;
for(int i=1;i<=n;i++)
{
if(vis[i]==0)
{
if(DFS(i))
{
flag=true;
break;
}
}
}
if(flag)
{
printf("Not DAG, fail\n");
return;
}
while(!result.empty())
{
printf("%d\n",result.top());
result.pop();
}
}
int main()
{
int n,m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(graph,0,sizeof(graph));
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
graph[a].push_back(b);
}
topoSort(n);
}
}