题目
给定一个n个点m条边的有向图,点的编号是1到n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-1。
若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。
输入格式
第一行包含两个整数n和m
接下来m行,每行包含两个整数x和y,表示存在一条从点x到点y的有向边(x, y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出-1。
数据范围
1≤n,m≤105
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
分析
拓扑排序,就是找入度为0的点入队列,弹出队列后将其出边连接点的入度减一。写法和BFS的模板差不多,因为都是对队列弹出结点的出边的结点操作。区别:队列弹出结点信息要记录,因为弹出结点的顺就是拓扑排序顺序。可以开一个数组记录弹出结点的信息,或者用数组模拟队列。
整体思路:
1.先找到所有入度为0 的结点入队列q
2.弹出队头元素q.front(),并记录这个结点
3.找到这个结点的所有出边的结点,使这些结点的入度减一,若入度减一后为0,入队列。
4.循环2,3步直到队列为空
代码
用 ans存放拓扑排序结果
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 7 const int N = 100010; 8 int h[N],e[N],ne[N],idx; 9 int n,m; 10 int d[N];//存放每个结点的入度 11 vector<int>ans;//记录拓扑排序结果 12 13 void add(int a,int b){ 14 e[idx] = b,ne[idx] = h[a],h[a] = idx++; 15 } 16 17 void topsort(){ 18 queue<int>q; 19 //寻找度为0的点 20 for(int i = 1;i <=n;i++){ 21 if(d[i] == 0){ 22 q.push(i); 23 } 24 } 25 26 while(!q.empty()){ 27 int t = q.front();ans.push_back(t); 28 q.pop(); 29 //将该元素出边的结点入度减一 30 for(int i = h[t];i!=-1;i = ne[i]){ 31 int j = e[i]; 32 d[j]--; //删掉边,入度减一 33 if(d[j] == 0){ 34 q.push(j); 35 } 36 } 37 } 38 39 } 40 41 int main(){ 42 scanf("%d%d",&n,&m); 43 memset(h,-1,sizeof(h)); 44 for(int i = 0;i < m;i++){ 45 int a,b; 46 scanf("%d%d",&a,&b); 47 add(a,b); 48 d[b]++; //b的入度加1 49 } 50 topsort(); 51 if(ans.size()!=n){ 52 cout<<"-1"; 53 }else{ 54 for(auto it:ans) cout<<it<<" "; 55 } 56 return 0; 57 }
用数组模拟队列,这样会保留弹出结点信息
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 6 const int N = 100010; 7 int h[N],e[N],ne[N],idx; 8 int n,m; 9 int d[N];//存放每个结点的入度 10 int q[N]; //用数组模拟队列 11 12 void add(int a,int b){ 13 e[idx] = b,ne[idx] = h[a],h[a] = idx++; 14 } 15 16 bool topsort(){ 17 int hh = 0, tt = -1; //定义对头,队尾 18 //寻找度为0的点 19 for(int i = 1;i <=n;i++){ 20 if(d[i] == 0){ 21 q[++tt] = i; //入队 22 } 23 } 24 25 while(hh <= tt){ 26 int t = q[hh++]; 27 28 //将该元素出边的结点入度减一 29 for(int i = h[t];i!=-1;i = ne[i]){ 30 int j = e[i]; 31 d[j]--; //删掉边,入度减一 32 if(d[j] == 0){ 33 q[++tt] = j; 34 } 35 } 36 } 37 return tt == n-1; //是否队列为n个点,来判断是否存在拓扑序列 38 39 } 40 41 int main(){ 42 scanf("%d%d",&n,&m); 43 memset(h,-1,sizeof(h)); 44 for(int i = 0;i < m;i++){ 45 int a,b; 46 scanf("%d%d",&a,&b); 47 add(a,b); 48 d[b]++; //b的入度加1 49 } 50 if(topsort()){ 51 for(int i = 0;i < n;i++) 52 printf("%d ",q[i]); 53 }else{ 54 printf("-1"); 55 } 56 return 0; 57 }