拓扑排序
对于一个有向无环图,简称DAG。进行拓扑排序,是将图G的顶点排成一个线性序列。在图的任意一点,对于一组边<u,v> ,满足u在线性序列中出现在v前面,我们称这样的序列为拓扑序列。
那么拓扑排序能解决什么问题呢???
比如:有先后顺序,存在并存关系的事情。我们可以采用拓扑排序来计算出这个线性序列。
下面我先来举个例子:
这是一个有向图,现在我们要做的就是,把它的先后顺序弄出来。
用肉眼可见,我们预期的样子是这样的:1->2->3->4->5->6
这个解并不是唯一的。也能是:2->1->3->4->5->6
只要保证顺序正确即可。
下面是重点:也是拓扑的思想
1、首先计算 入度为0的顶点 (不知道入度的同学,推荐一篇博客入度介绍博客)
2、删除入度为0的边,并且把删除的点 保存起来,因为我们要求这个序列嘛。
循环结束后,循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,或者入度的存在不为0的,那么也存在回路。
下面,我们来模拟一下拓扑排序:
第一次入度为0的是1号节点,那么我们把1号节点存入,并且删除它连的所有的边。
第二个点是2号节点,也和上述操作一样,然后剩下3,4,5,6这几个点也是相同操作。
下面来解释代码:
首先要准备一个数组,用来记录入度。存图的方式采用vector数组来存,也能用链式前向星来存。然后搞一个队列,来存放入度为0的节点。
然后,每次pop出来,然后每次入度减减。只要入度为0了,那么说明什么,要入队列了,保证队列里的点都是入度为0的点,这样就保证了先后顺序,想想看,是不是这样的。
话不多说,代码先上:
#include <bits/stdc++.h>
using namespace std;
int const N=1e5+100;
int d[N];//每个点的入度
vector<int>G[N];//用来存图
queue<int>q;//队列q
int n,m;//n个节点,m组边
int u,v;//边u,v
vector<int>ans;//用来存拓扑序
int main()
{
//读入
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);//把u相连的边v,存到一个vector
d[v]++;//v点的入度++
}
for(int i=1;i<=n;i++)
{
if(d[i]==0)//如果入度为0,那么我们就丢到队列里
{
q.push(i);
}
}
while(!q.empty())
{
int now=q.front();//弹出一个入度为0的点
ans.push_back(now);//把拓扑序记录下来
q.pop();//出队
int len=G[now].size();
for(int i=0;i<len;i++)//遍历该点的所有边
{
d[G[now][i]]--;//每次把入度--
if(d[G[now][i]]==0)//如果度为0了,那么我们就入队
{
q.push(G[now][i]);
}
}
}
//上述操作就完成了拓扑排序了
//输出拓扑序
int len=ans.size();
for(int i=0;i<len;i++)
{
printf("%d%c",ans[i],i==len-1?'\n':' ');
}
/*输入:
6 7
1 3
3 6
1 4
4 6
2 4
2 5
5 6
*/
//输出:1 2 3 4 5 6
return 0;
}
拓扑排序怎么判断当前有向图 是否存在环呢 ?
:如果拓扑的节点数量和总数量不一样,就说明存在环了。