拓扑排序
介绍
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。
通俗讲就是按照若干个a必须在b前这样的规则将一组数据排序
我觉得拓扑排序是一种结果而不是一个过程。他不想冒牌排序、选择排序那种直接给出了排序的操作步骤,拓扑排序只是给出了排序的结果(对排序的要求),并没有给出排序的操作步骤。
可以使用拓扑排序来判断图中有没有环
拓扑排序的方法
方法一:使用最简单的dfs实现
每次搜索到出度为0的点返回,便返回边输出;
#include <iostream>
#include <vector>
using namespace std;
const int MAXSIZE = 1e2 + 5;
int vis[MAXSIZE]; //储存每个点个访问状态, 0 表示未访问 -1 表示正在访问 1表示 已访问过
int topo[MAXSIZE]; //储存排序的结果。
vector<int> G[MAXSIZE]; //储存拓扑关系
int n,m,t;
bool toposoft(int u)
{
vis[u] = -1; // 标记当前点正在访问
for(auto v:G[u]) // 遍历当前点u的每个可到达点
{
if(vis[v] == -1) return false; // 如果下一个已经访问过说明此图存在环。
if(!vis[v]) toposoft(v);
}
vis[u] = 1;
topo[t--] = u; // 逆序保存,就能正序输出拓扑序了
}
int main()
{
//读入数据
/*
拓扑关系的储存; 对于输入的每组a,b; a->b;
G[a].emplace_back(b);
*/
t = n;
for(int i=1;i<=n;i++) if(!vis[i]) toposoft(i);
return 0;
}
方法二:使用队列或者优先队列实现
优先队列可以保证输出的结果是字典序最小或最大之类的.
如果题目没有要求(输出任意一组符合题意的就行) 那么用普通队列就可以
思路就是将所有入度为0的点放入队列中, 每次取出队首元素将该元素的所有相邻节点入度减一,如果入读变成了零就放入队列,重复操作直到队列为空.
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using std::vector;
using std::greater;
using std::priority_queue;
const int MAXSIZE = 500 + 5;
int indegree[MAXSIZE];
priority_queue<int,vector<int>,greater<int>> pq;
int G[MAXSIZE][MAXSIZE];
int n,m,x,y;
void toposoft()
{
while(!pq.empty()) pq.pop();
int top;
for(int i=1;i<=n;i++)
{
if(indegree[i] == 0) pq.push(i);
}
bool flag = true;
while(!pq.empty())
{
top = pq.top();
pq.pop();
indegree[top] = -1;
if(flag)
{
printf("%d",top);
flag = false;
}else printf(" %d",top);
for(int i=1;i<=n;i++)
{
if(G[top][i])
{
indegree[i]--;
if(indegree[i] == 0) pq.push(i);
}
}
}
printf("\n");
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(G,0,sizeof(G));
memset(indegree,0,sizeof(indegree));
if(m+n == 0) break;
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
if(G[x][y] == 0)
{
G[x][y] = 1;
indegree[y]++;
}
}
toposoft();
}
return 0;
}