本文章同步发表于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 DAG,Directed 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;
}