拓扑排序(topo-sort)

本文介绍了如何在C++中使用队列实现有向无环图(DAG)的拓扑排序,通过选择入度为0的顶点开始,递归地更新邻接表并输出拓扑序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章同步发表于cnblogs

前置芝士

在图论中,如果一个有向图从任意顶点出发无法经过一条路径回到该点,则这个图是一个有向无环图( D A G , D i r e c t e d   A c y c l i c   G r a p h \tt DAG,Directed\ Acyclic\ Graph DAGDirected Acyclic Graph)。

  • 出度:一个点指向了多少个点;
  • 入度:一个点被多少个点指向。

举个例子:
A A A 指向点 B B B,则点 A A A 的出度 + 1 +1 +1,点 B B B 的入度 + 1 +1 +1

正式内容

对于一个 D A G \tt DAG DAG,每个顶点只能出现一次。 即如果存在一条点 A A A B B B 的路径,点 A A A 指向点 B B B,也就是 A A A 位于 B B B 前,那么我们称之为拓扑序

注意:拓扑序不一定是唯一的。

那么对于一个 D A G \tt DAG DAG 来说,怎么才能算法实现呢?

有以下几点:

  • D A G \tt DAG DAG 图中选择入度为 0 0 0 的顶点并输出。
    代码中我们可以在每一次询问时统计入度 0 0 0 的点,把他进入一个队列或栈(我这里用的是队列),等到处理这个点时,再把它出队。
    预处理我们这样写:

    queue<int> q;
    for (int i = 1 ; i <= n ; i++)
    {
    	if (in[i] == 0)
    	{
    		q.push(i);
    	}
    }
    

    输出的话只需输出每个队头即可,在下面会提到。

  • 从图中删除这个节点以及由他发出的有向边,同时该节点入度减一。

    while(q.empty() == false)//只要图不为空,就要继续找点。
    {
    	int cur = q.front();//提取队头。
    	q.pop();//注意弹出,不然死循环。
    	cout << cur << " ";//输出。
    	for (int i = 0 ; i < nbr[cur].size() ; i++)//auto nxt : nbr[cur],这里的写法仅适用于c++11以上,可以在DEVc++的控制台上输出-std=c++版本,例如:c++11。
    	{
    		int nxt = nbr[cur][i];
    		in[nxt]--;//当前点入度--,相当于删点操作。
    		if (in[nxt] == 0)//注意,是nxt
    		{
    			q.push(nxt);//入队
    		}
    	}
    }
    
  • 重复第一步和第二步,直到当前图为空。

代码设计还有一个注意事项,就是如何建图,如果是点 A A A 指向 点 B B B,那么要将 B B B 的入度 + 1 +1 +1

nbr[a].push_back(b);
in[b]++;

这里用向量 vector 来直接存。

完整代码:

#include<bits/stdc++.h>
using namespace std;
vector<int> nbr[205];
int n, in[205]; 
void topo_sort()
{
	queue<int> q;
	for (int i = 1 ; i <= n ; i++)
	{
		if (in[i] == 0)
		{
			q.push(i);
		}
	}
	while(q.empty() == false)
	{
		int cur = q.front();
		q.pop();
		cout << cur << " ";
		for (int i = 0 ; i < nbr[cur].size() ; i++)//auto nxt : nbr[cur]
		{
			int nxt = nbr[cur][i];
			in[nxt]--;
			if (in[nxt] == 0)
			{
				q.push(nxt);
			}
		}
	}
	return ;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1 ; i <= n ; i++)
	{
		int opt;
		while(cin >> opt)
		{
			if (opt != 0)
			{
				nbr[i].push_back(opt);
				in[opt]++;
				continue;
			}
			break;
		}
	}
	topo_sort();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值