拓扑排序

拓扑排序

对于一个有向无环图,简称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;
}

拓扑排序怎么判断当前有向图 是否存在环呢 ?

:如果拓扑的节点数量和总数量不一样,就说明存在环了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值