有向图的拓扑序列 题型:图论

127 阅读2分钟

848. 有向图的拓扑序列 - AcWing题库

解析

什么是拓扑序?

拓扑序是指每一个起点一定在终点前,列如下面这种图:

image.png

如果是这样的话就不是拓扑图了:

这样的话3就在1前面了

image.png

因此不难发现,拓扑图都是一个有向无环图。

证明

反证法,我们去证明一个有环图一定不是拓扑序列。

对于拓扑图的每一个点都一定有一个点有0个入度。

如果是一个有环图,那么该图一定不是拓扑图(因为有环图中每个节点都一定至少有1个进度)。

image.png 证毕

拓扑图的特点

对于一个有向无环图一定有一个入度为0的点,像下面这个拓扑图中1这个节点就没有入度: image.png

因此所有入度为0的点,我们都可以将其视为一个起点。

1.我们可以用一个队列,我们先将所有入度为0的点入队,

2.然后枚举所有的出边t~x

3.运用bfs从所有入度为0的点开始突破队列,并且将该点指向的所有节点的入边+1,若减完之后d==0,就将该点入队列。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N];  //头节点
int e[N];  //边值
int ne[N]; //指向下一个节点的指针
int idx;    //地址
bool st[N]; //标记,防止一个点被走多次
int q[N];
int d[N]; //入度






bool topsort()
{
	int hh = 0, tt = -1;  //因为队列为空,所以从-1开始,要往队列中加入元素的时候先++,这样就会到0了,表示队头
	for (int i = 1; i <= n; i++)
	{
		if (!d[i])  //如果没有入度
		{
			q[++tt] = i;  //加入队列
		}
	}


	while (hh <= tt)
	{
		int t = q[hh++];

		for (int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			d[j]--;   
			if (d[j] == 0)  q[++tt] = j;
		}
	}

	return tt == n - 1;
}

void add(int a, int b)
{
	e[idx] = b;  //把b赋值给边值
	ne[idx] = h[a];  //头结点指向下一个节点
	h[a] = idx++;  //头节点跳到下一个节点


}

int main()
{
    cin >> n >> m;
	memset(h,-1,sizeof h);

	for (int i = 0; i < m; i++)
	{
		int a, b; cin >> a >> b;
		add(a, b);
		d[b]++;   //一条a指向b的边,b的入度++
	}

	//判断一下是否是拓扑序
	if (topsort())
	{
		for (int i = 0; i < n; i++)
		{
			cout << q[i] << " ";
		}cout << endl; 
	}
	else
	{
		cout << -1 << endl;
	}

	return 0;
}