拓扑排序-java实现
有向图的拓扑序列
思路
有向无环图一定存在一个拓扑序列,用队列来做,每次存储每个点的入度,拓扑排序的起点是入度为0的点,用队列来解决这个问题,每次发现某点的入度为 0,所以删除该点和该点上所连的边,继续检查入度为0的点,循环(不清楚的可以自己画个图比如 a->b a->c b->d c->d这样的图)。队列顺序就是拓扑排序
queue<-所有入度为0的点
while queue 不空{
取出队头t
枚举t的所有出边 t->j删掉t->j: d[j]--if d[j]==0queue<-j
}
代码
import java.util.*;
public class Main{static int N = 100010,n,m,hh,tt,idx;static int[] e = new int[N],ne = new int[N],h = new int[N];static int[] q = new int[N];static int[] d = new int[N];public static void add(int a,int b){e[idx] = b;ne[idx] = h[a];h[a] = idx++;}public static boolean bfs(){hh = 0 ; tt = -1;for(int i = 1 ; i <= n ; i ++ ){ if(d[i] == 0){ //首先将所有入度为0的点全部插入q队列中q[++tt] = i;}}while(hh <= tt){int t = q[hh++]; //拿出队头for(int i = h[t] ; i != -1; i = ne[i]){ //遍历一下队列中所有的边int s = e[i]; //然后将这个边拿出来d[s] -- ; //将这个边的入度数减1if(d[s] == 0){q[++tt] = s; //如果减完之后s的入度数为0;就将他插入队列中} }}return tt == n - 1; //最后返回,如果队列中的tt等于n-1个数,说明就是正确的的}public static void main(String[] args){Scanner scan = new Scanner(System.in);n = scan.nextInt();m = scan.nextInt();for(int i = 0 ; i < N ; i ++ ){h[i] = -1; }while(m -- > 0){int a = scan.nextInt();int b = scan.nextInt();add(a,b);d[b] ++;}if(bfs()){//队列刚好队头删除的点就是我们的拓扑序列,因为我们只是将hh往后面移动,但是它具体前面的值还在,直接输出就行for(int i = 0 ; i < n; i ++ ){ System.out.print(q[i] + " ");}}else{System.out.println("-1");}}
}
奇怪的知识增加了 用System.out.printf(q[i] + " ");会超时,写算法的时候尽量用print和println
读取输出更快的写法
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;public class Main {static int N = 100010, idx, hh, tt, n, m;static int[] h = new int[N];static int[] e = new int[N];static int[] ne = new int[N];static int[] d = new int[N]; // 存储每个节点的入度static int[] q = new int[N]; // 数组模拟队列(拓扑排序用)// 邻接表添加边:a → bpublic static void add(int a, int b) {e[idx] = b;ne[idx] = h[a];h[a] = idx++;}// Kahn算法实现拓扑排序public static boolean topsort() {hh = 0;tt = -1;// 所有入度为0的节点入队for (int i = 1; i <= n; i++) {if (d[i] == 0) {q[++tt] = i;}}// 队列不为空时,处理节点while (hh <= tt) {int t = q[hh++]; // 取出队首节点// 遍历该节点的所有邻接节点for (int i = h[t]; i != -1; i = ne[i]) {int j = e[i];d[j]--; // 邻接节点入度减1if (d[j] == 0) { // 入度为0时入队q[++tt] = j;}}}// 若队列长度等于节点数,说明存在拓扑排序(无环)return tt == n - 1;}public static void main(String[] args) throws IOException {// 用BufferedReader替代Scanner,提升输入效率BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StringTokenizer st = new StringTokenizer(br.readLine());// 读取n(节点数)和m(边数)n = Integer.parseInt(st.nextToken());m = Integer.parseInt(st.nextToken());// 初始化邻接表表头为-1(表示空链表)for (int i = 1; i < N; i++) {h[i] = -1;}// 读取m条边,构建邻接表并统计入度while (m-- > 0) {st = new StringTokenizer(br.readLine());int a = Integer.parseInt(st.nextToken());int b = Integer.parseInt(st.nextToken());add(a, b);d[b]++; // b的入度加1}// 执行拓扑排序并输出结果if (topsort()) {// 用StringBuilder拼接输出,避免多次System.out.printStringBuilder sb = new StringBuilder();for (int i = 0; i < n; i++) {sb.append(q[i]).append(" ");}System.out.println(sb.toString().trim());} else {System.out.println(-1);}br.close(); // 关闭流资源}
}