题目链接
思路:拓扑排序(深搜)
拓扑排序的定义:给定一个包含n个节点的有向图G,我们给出它的节点编号的一种排序,如果满足,对于图G中的任何一条有向边(u,v),u都在排列中都出现在v的前面,那么就称该排列是图G的【拓扑排序】。
根据这个定义,可以得出两个结论:
- 存在拓扑排序的图,肯定是有向无环图。即存在拓扑排序的图,肯定不能有环。
- 一个有向无环图,他的拓扑排序可能有好多种,例如所有节点都没有边,那么任意的编号排列都是一种拓扑排序。
这个题目的意思就是求一个图的拓扑排序,前提是如果存在的话。
首先:将题目给的图,用邻接链表存储起来。
举例:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
那么邻接链表如下图:
[u,v]:表示u到v存在一条有向边,但是我们遍历的时候是先访问最后的节点的。所以这里才表示为u到v的有向边。
我们将图存储起来后。
使用一个标志记录当前图是否有环,有环的判断就是指从某个点出发,经过一系列的遍历,又回到了这个点,如果有环,那么就不存在拓扑排序。
使用一个数组标志某个点是否被遍历过,如果遍历过,那么就不再遍历。
并且每个点有三种状态。(正在遍历中,遍历结束,还未遍历)。
代码:
class Solution {
List<List<Integer>> list = null;
//1搜索中 2 已经搜索过 0 未搜索
int[] visited = null;
//numCourses-1;
int[] res = null;
int index = -1;
//是否有环
boolean valid = true;
public int[] findOrder(int numCourses, int[][] prerequisites) {
if(numCourses==1){
return new int[]{0};
}
visited = new int[numCourses];
list = new ArrayList<>(numCourses);
res=new int[numCourses];
index = numCourses-1;
for(int i=0; i<numCourses; i++){
list.add(new ArrayList<>());
}
//邻接链表存储
for(int[] pre : prerequisites){
list.get(pre[1]).add(pre[0]);
}
//挑选 没被搜索过的节点进行dfs
for(int i=0;i<numCourses;i++){
if(visited[i]==0){
dfs(i);
}
}
if(!valid){
//说明有环,有环肯定不行
return new int[]{};
}
return res;
}
private void dfs(int start){
if(!valid){
//有环直接返回
return;
}
if(visited[start]==1){
//形成了环
valid = false;
return;
}
//将当前节点的状态改为正在搜索
visited[start] = 1;
//取出该节点的链表
List<Integer> seqr = list.get(start);
//搜索该链表中的节点
for(int i=0;i<seqr.size();i++){
if(visited[seqr.get(i)]==2){
//已经被搜索过了就不搜索了
continue;
}
dfs(seqr.get(i));
}
//将当前节点改为搜索完成
visited[start] = 2;
//加入答案
res[index--] = start;
}
}