不必太过在意标题,拓扑排序只不过是排序的一种:每个节点都向后指向进而形成的结构,即对于图中的每条边 (x,y),x 都出现在 y 之前。
拓扑序列
一定是针对有向图来说的,无向图没有这一说法。
如果一个图形成了闭环,那么一定不是拓扑图。那么有向无环图一定存在一组拓扑序列。
度数:入度、出度。
入度:是指有多少节点形成的边指向该节点。出度:是指该节点有多少条边指向其他节点。
那么任意一个入度为0的点都可以作为起点。
对于这样一个有向图来说:1的入度为0,出度为2。2的入度为1,出度为1。3的入度为2,出度为0。
那么1就可以作为起点。
题目:
思路(伪代码)
入度为0的点入队,如果有环的话,那么这个环上一定没有入度为0的点
//宽搜
while(队列不空)
{
int t=q.front();
//枚举t的所有出边t→j
//删掉t→j这条边(因为t为队头即在最前面,所以t→j这条边一定满足拓扑序列)
//这时候j的入度会减少,所以让j的入度减去1
if(j的入度为0)
{
那么j就可以作为队头了。
//j入队
q.push(j);
}
}
代码:
数组模拟:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx;
int n,m;
int d[N],q[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topo()
{
int hh=0,tt=0;
//数值是:1~n
for(int i=1;i<=n;i++)
{
if(!d[i])
q[tt++]=i;//队列记录度数为0的节点的索引
}
while(hh<=tt)
{
int t=q[hh++];//取出头节点的索引
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];//t指向j形成的一条边
d[j]--;//j的入度会少1
if(!d[j])
{
q[tt++]=j;//入队
}
}
}
//如果是一个拓扑顺序的话
return tt==n;
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
//m次询问
for(int i=0;i<m;i++)
{
int a,b;
cin >> a >> b;
add(a,b);
//a指向b,所以b的入度数+1
d[b]++;
}
if(topo())
{
//入队的顺序即为拓扑的顺序
for(int i=0;i<n;i++) cout << q[i] << " ";
}
else cout << "-1";
return 0;
}
队列:
~~慢慢的这种题连暴力都不会写了~~