直观地说就是,让你把一幅图「拉平」,而且这个「拉平」的图里面,所有箭头方向都是一致的,比如上图所有箭头都是朝右的。
对于任何有向图而言,其拓扑排序为其所有结点的一个线性排序(对于同一个有向图而言可能存在多个这样的结点排序)。该排序满足这样的条件——对于图中的任意两个结点u和v,若存在一条有向边从u指向v,则在拓扑排序中u一定出现在v前面。
拓扑排序可以用来判断图中是否有环, 还可以用来判断图是否是一条链。
算法思路
拓扑排序是基于 有向图 无环图的
假如你是一个编译器,你需要编译以下命令。
A B C D E 这几条命令。
A是依赖B,C的,C依赖D,E,B依赖E,这时候你需要找到一条不需要依赖其他命令的那条命令首先编译他,之后的才能编译。
这时候我们用拓扑排序就可以找到依赖关系最小的那个顶点在这个图里就是D E。依次排序输出。
思路
找到入度为0的点,然后把他储存后在图里删掉。
那么这个点的nexts里面的点入度就减少1
继续找入度为0的点重复上述步骤。
####算法的具体设计
#####实现一:Kahn算法(BFS广度优先遍历)
保存他的所有节点对应的入度。
用一个队列保存每次入度为0的节点然后pop,pop的顺序就是拓扑排序的顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class topoSort{
public void BFS(List<List<Integer>> adjacency){ Queue<Integer> queue = new LinkedList<>(); int n = adjacency.size(); int[] indegrees = new int[n]; for(List<Integer> nodes: adjacency){ } for(int i = 0;i<n;i++) if(indegrees[i] == 0) queue.add(i);
while(!queue.isEmpty()){ int pre = queue.poll(); System.out.println(pre); n--; for(int cur: adjacency.get(pre)){ if(--indegrees[cur] == 0) queue.add(cur); } } } }
|
####实现二:DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class Solution{
public boolean canFinish(int numCourses,int[][] prerequisites){ List<List<Integer>> adjacency = new ArrayList<>(); for(int i = 0;i<numCourses;i++) adjacency.add(new ArrayList<>()); int[] flags = new int[numCourses]; for(int[] cp: prerequisites) adjacency.get(cp[1]).add(cp[0]); for(int i = 0;i<numCourses;i++) if(!dfs(adjacency,flags,i)) return false; return true; }
private boolean dfs(List<List<Integer>> adjacency,int[] flags,int i){ if(flags[i] == 1) return false; if(flags[i] == -1) return true; flags[i] = 1; for(Integer j: adjacency.get(i)) if(!dfs(adjacency,flags,j)) return false; flags[i] = -1; return true; } }
|