拓扑排序:
首先拓扑排序适用于aov网,就是有向无环图。
这里插入一下链接:比较容易去进行理解。
top排序的图形讲解
结合王道的内容讲解去解释一下拓扑排序的条件:
(1)每个顶点出现且仅仅出现一次
(2)若顶点A在序列中,排在顶点B的前面,则在图中不存在从顶点B到顶点A的路径
算法流程:
统计所有节点的入度,对于入度如果为0的点分离出来。
如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。
算法流程:
用队列来执行 ,初始化讲所有入度为0的顶点入队。
主要由以下两步循环执行,直到不存在入度为 0 的顶点为止
(1)选择一个入度为 0 的顶点,并将它输出;
(2)删除图中从顶点连出的所有边。
循环结束,
若输出的顶点数小于图中的顶点数,则表示该图存在回路,即无法拓扑排序,
否则,输出的就是拓扑序列 (不唯一)
虽然看起来很长,但是我觉得真的还是比较好理解的。
这里我顺便结合图去讲解一下:虽然不太建议去用特例去解释:
分析过程如下:
在a图中,我们不难发现入度=0的点为a和f。那么,如果存在拓扑排序的话,那么可能就要通过a和f或者拓扑序列。
得到拓扑过程如下(其实真的很类似于层序遍历):
我们拿到队首的元素,我们依次遍历单链表:a->c(此时再减去入度,那么如果d[c]==0,那么就是拓扑序列)->b.
注意为什么不先从a->b,因为b的入度为2,显然不是拓扑序列的一个小过程。
同理可以得出fde是另外一个环的拓扑序列。
//topsort的分析
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N=100010;
int n,m;
int h[N],e[N],ne[N],idx;
int d[N];//记录每一个点的入度
int top[N];//存储top序列
//头插法进行链表的插入
int cnt=1;//记录入队里面的个数
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool topsort(){
queue<int> q;
int t;
//先把入度为0的点全部放在队列里面
for(int i=1;i<=n;i++){
if(d[i]==0) q.push(i);
}
//以上肯定就是一个个顶点,因为顶点的度都是为0
//然后从度为0的点开始去进行一次遍历,其实就是一个链表的遍历
while(q.size()){
t=q.front();//取出队首的元素
//根据队首的首部然后把首元素取出,然后一次遍历里面,存储起来
top[cnt++]=t;
q.pop();
//根据的元素去遍历整条链表,删除图中从顶点连出的所有边
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
d[j]--;
if(d[j]==0) q.push(j);
}
}
//但是还有可能是存在单个或者多个环,那么就是不存在环了]
if(cnt<n) return 0;
else return 1;
}
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]++;//b的度+1
}
//拓扑排序注意一下出度和入度就行了
//现在要求做的是,如果不是拓扑排序,那么就输出-1
//如果是拓扑排序的话,那么输出拓扑排序的过程
if(topsort()==0) puts("-1");
else{
for(int i = 1;i <= n; ++i){
cout << top[i] <<" ";
}
}
return 0;
}
例题还在做,稍后补充!