拓扑排序
一、什么是拓扑排序
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
每个顶点出现且只出现一次。
若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
那么如何在一个DAG中找出他的拓扑排序呢?
1.从DAG图中选择一个入度为0的点并输出
2.删除这个点及与它相连的边
3.重复进行12知道DAG图为空图或当前图中不存在入度为0的点为止(此时图自环)
应该很生动形象了吧hhhh
一个DAG图中可以有一个或多个拓扑排序,比如上图中就还有另外的拓扑排序
二、拓扑排序的应用
通常用来“排序”具有依赖关系的任务
如工程建设中需要先做什么,再做什么的任务,比赛只告诉你谁打败了谁,让你排出名次的任务
三、代码实现
来填坑了!
代码实现的话,思路是存一下图,然后开一个数组存每个点的入度,再开一个队列q,将入度非零的点入队,从队列顶端取出一个顶点,删除与这个顶点相连的边,将该顶点出队然后对下一个入度为0的点出队即可。
那么我们来看一个题嘿嘿嘿
uva 10305 Ordering Tasks
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oOYyhUaB-1578809825067)(image/uva10305.png)]
题意大概就是给你n个点,m条边,接下来m行个a,b。需要你求出它的拓扑排序中的一种(此题可以有多种解只需要求出一组解),此题的输入保证了不会有自环的情况。
那么这就是一个简单的模板题,数据量很小用邻接矩阵存图也可
给出邻接矩阵的ac代码:
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
const int N = 505;
int n, m, in[N], a[N];
int mp[N][N];
void tp()
{
queue<int> q;
int l = 0;
while (!q.empty()) q.pop();
for (int i = 1; i <= n; i++)
if (!in[i]) q.push(i); //入度非0,入队
int temp = q.front();
while (!q.empty()) {
a[l++] = temp;//存结果
q.pop();
for (int i = 1; i <= n; i++) {
if (mp[temp][i]) {
in[i]--;
if (!in[i]) q.push(i);
}
}
temp = q.front();
}
for (int i = 0; i < n; i++) cout << a[i] << " ";
cout << endl;
}
int main()
{
while (cin >> n >> m) {
if (n == m && n == 0) break;
memset(mp, 0, sizeof(mp));
memset(in, 0, sizeof(in));
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
if (!mp[a][b]) {//存图
mp[a][b] = 1;
in[b]++;
}
}
tp();
}
}
那么在实际的做题中,存图肯定是用邻接表的,这里是邻接表存图的做法:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
vector<int> g[N];
int in[N];
queue<int> q;
void topo(){
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++)
if(!in[i]) q.push(i);
int sum=1;
while(!q.empty()){
int temp=q.front();
q.pop();
cout<<temp<<" ";
for(int i=0;i<g[temp].size();i++){//删除边
in[g[temp][i]]--;
if(in[g[temp][i]]==0) q.push(g[temp][i]);
}
}
cout<<endl;
}
int main(){
while(cin>>n>>m){
if(n==m&&n==0) break;
for(int i=0;i<=n;i++) g[i].clear();
memset(in,0,sizeof(in));
if(n==0&&n==m) break;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
in[b]++;
}
topo();
}
return 0;
}
还有常见的输出字典序最小的拓扑排序,只需要使用一个优先队列即可
hdu1285
struct cmp1{
bool operator()(int &a,int &b){
return a>b;
}
};
priority_queue<int,vector<int>,cmp1>q;
ac代码:
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
vector<int>g[550];
int indegree[550];
int ss[550];
int ans;
struct cmp1{
bool operator()(int &a,int &b){
return a>b;
}
};
int topo(int n){
priority_queue<int,vector<int>,cmp1>q;
int i;
for(i=1;i<=n;i++){
if(indegree[i]==0)
q.push(i);
}
int u,gg;
ans=0;
gg=0;
ans=0;
while(!q.empty()){
if(q.size()!=1)gg=1;
u=q.top();q.pop();
ss[++ans]=u;
for(i=0;i<g[u].size();i++){
indegree[g[u][i]]--;
if(indegree[g[u][i]]==0)q.push(g[u][i]);
}
}
if(ans!=n)return -1;//无法排序
if(gg==1)return 2;//仅有一种排序方式
return 1;
}
int main(){
int f1,f2,t2;
int i,n,m;
while(cin>>n>>m){
if(n==m&&n==0) break;
memset(indegree,0,sizeof(indegree));
memset(g,0,sizeof(g));
memset(ss,0,sizeof(ss));
for(i=1;i<=m;i++){
cin >> f1 >> f2;
indegree[f2]++;
g[f1].push_back(f2);
}
t2=topo(n);
for(i=1;i<ans;i++)
cout << ss[i] << " ";
cout << ss[i] << endl;
}
return 0;
}
参考博客
https://blog.csdn.net/lisonglisonglisong/article/details/45543451