判断是不是有向无环图-拓扑排序

有向无环图-拓扑排序

题意
在一个QQ群里有着许多的师生关系,如A 是B的师傅,同时B是A的徒弟,一个师父可以有许多徒弟,一个徒弟也可以有许多师父。输入该群里所有的师徒关系,问是否存在这样一种非法的情况:以三个人为例,即A是B的师父,B是C的师父,C反过来又是A的师父。

若我们将群里所有人都抽象成图上的节点,将师徒关系抽象成有向边(由师父指向徒弟),则该实际问题就转化为一个数学问题,判断该图是不是有向无环图–拓扑排序。

/*拓扑排序定义:
对一个有向无环图,所有的有向边为(U,V)(由U指向V),在拓扑排序中节点U都排列在节点V之前,
即若节点U经过若干条有向边后能够到达节点V,则在求得的序列中U必排在V之前*/

/*如何求一个有向无环图的拓扑序列即拓扑排序的方法:
选择一个入度为0的节点,作为序列的第一个节点,
当该节点被选为序列的第一个顶点后,将该点从图中删去,同时删去以该节点为弧尾的所有有向边,得到一个新图,重复以上操作。
若所有的节点尚未被删去时即出现了找不到入度为0的节点,则说明剩余的节点形成一个环路,拓扑排序失败,原图不存在拓扑序列*/
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int> edge[501];//邻接链表,因为边不存在权值,只需保存与其邻接的节点编号即可
queue<int> Q;//保存入度为0的节点的队列
int main(){
	int inDegree[501];//存储每个节点的入度
	int n, m;
	cin >> n >> m;
	if (n == 0 || m == 0){
		return 0;
	}
	for (int i = 0; i < n; i++){
		inDegree[i] = 0;//初始化入度信息
		edge[i].clear();//清空邻接链表
	}
	int a, b;
	while (m--){
		cin >> a >> b;//读入一条由a指向b的有向边
		inDegree[b]++;//累加b的入度
		edge[a].push_back(b);//将b加入a的邻接链表
	}
	while (Q.empty() == false){//清空队列
		Q.pop();
	}
	for (int i = 0; i < n; i++){
		if (inDegree[i] == 0){
			Q.push(i);//若节点入度为0,则将其放入队列
		}
	}
	int cnt = 0;//累加已经确定的拓扑排序的节点个数
	while (Q.empty() == false){
		int nowP = Q.front();
		Q.pop();
		cnt++;
		for (int i = 0; i < edge[nowP].size(); i++){
			inDegree[edge[nowP][i]]--;//删除该节点指向的所有边,即该节点所有后继节点的入度减一
			if (inDegree[edge[nowP][i]] == 0){//若该节点入度为0,则加入队列
				Q.push(edge[nowP][i]);
			}
		}
	}
	if (cnt == n){//若所有节点都能被确定为拓扑序列,则原图为有向无环图
		cout << "YES" << endl;
	}
	else{
		cout << "NO" << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值