一、题目
描述
给定一个包含n个点m条边的有向无环图,求出该图的拓扑序。若图的拓扑序不唯一,输出任意合法的拓扑序即可。若该图不能拓扑排序,输出-1。
输入描述:
第一行输入两个整数n,m ( 1≤n,m≤2⋅10^5),表示点的个数和边的条数。接下来的mm行,每行输入两个整数ui,vi(1≤u,v≤n),表示ui到vi之间有一条有向边。
输出描述:
若图存在拓扑序,输出一行n个整数,表示拓扑序。否则输出-1。
二、代码
#include <stdio.h>
#include <stdlib.h>
// 图中节点的结构,分别为邻接点(表示为数组)、编号、入度、出度
typedef struct vNode{
struct vNode** adj;
int index;
int inNum;
int outNum;
}vNode;
int main() {
int nodeNum, edgeNum;
scanf("%d%d", &nodeNum, &edgeNum);
int cover[nodeNum]; // i点是否已经被处理
struct vNode* gragh[nodeNum];
// 初始化图中节点
for(int i = 0; i < nodeNum; i ++){
gragh[i] = (struct vNode*)malloc(sizeof(struct vNode));
gragh[i]->index = i;
gragh[i]->inNum = 0;
gragh[i]->outNum = 0;
gragh[i]->adj = NULL;
cover[i] = 0;
}
// 边的输入
int v1, v2;
for(int i = 0; i < edgeNum; i ++){
scanf("%d%d", &v1, &v2);
v1--;
v2--;
gragh[v2]->inNum ++;
gragh[v1]->adj = realloc(gragh[v1]->adj, sizeof(struct vNode*) * (++ gragh[v1]->outNum));
gragh[v1]->adj[gragh[v1]->outNum - 1] = gragh[v2];
}
int doneV = 0;
int result[nodeNum]; // 存放结果
while(doneV != nodeNum){
int stillUpdate = 0; // 标志是否可以退出循环(如果遍历了一遍以后没有要输出的节点,那么停止)
for(int i = 0; i < nodeNum; i ++){
if(cover[i] == 1){
continue;
}
if(gragh[i]->inNum == 0){
result[doneV ++] = i + 1;
stillUpdate = 1;
cover[i] = 1;
// printf("delete %d cover:%d, doneV:%d, outNum %d\n", i+1, cover[i], doneV, gragh[i]->outNum);
for(int j = 0;j < gragh[i]->outNum; j ++){
gragh[i]->adj[j]->inNum --;
}
// 记得释放内存
free(gragh[i]);
gragh[i] = NULL;
}
}
if(!stillUpdate){
break;
}
}
if(doneV == nodeNum){
for(int i = 0; i < nodeNum-1; i ++){
printf("%d ", result[i]);
}
printf("%d", result[nodeNum - 1]);
}else{
printf("-1");
}
return 0;
}
三、总结
很久没有做和图相关的题了,有点生疏,这里用一个数组存放图中的每一个节点,每个节点的邻接点也用数组存放,同时记录每个节点的入度出度和编号。
首先是一个大循环,在大循环里,循环遍历每一个点,输出入度为0的点,如果一个大循环下来,没有要输出的点,那么退出大循环。