描述
在有向图中,以某个节点为起始节点,从该点出发,每一步沿着图中的一条有向边行走。如果到达的节点是终点(即它没有连出的有向边),则停止。
对于一个起始节点,如果从该节点出发,无论每一步选择沿哪条有向边行走,最后必然在有限步内到达终点,则将该起始节点称作是安全的。
返回一个由图中所有安全的起始节点组成的数组作为答案。答案数组中的元素应当按升序排列。
分析
题目是想找出所有不管怎么选择都一定会走到一个出度为0的结点。
拓扑排序是一个从入度为0的结点开始,不断弹出,诞生新的入度为0的结点,再弹出,如此往复直到没有结点。如果还有节点说明存在环。
题目是找一定会走到一个出度为0的结点。逆向思维,把图中的指向反转,即原来是一定会走到求出度为0的结点变成通过不断弹出入度为0的结点一定会变成入度为0的结点,这样可以利用拓扑排序,找到这些节点。
反转方向很简单,与平常建立邻接表的过程基本一致。
class Solution {
public List<Integer> eventualSafeNodes(int[][] graph) {
int n = graph.length;
List<List<Integer>> ngraph = new ArrayList<>();
int[] degree = new int[n];
Queue<Integer> que = new LinkedList<>();
List<Integer> res = new ArrayList<>();
for(int i = 0; i < n; i++){
ngraph.add(new ArrayList<>());
}
for(int i = 0; i < n; i++) {
for(int num : graph[i]){
ngraph.get(num).add(i);
degree[i]++;
}
}
for(int i = 0; i < n; i++){
if(degree[i] == 0){
que.add(i);
}
}
while(!que.isEmpty()){
int size = que.size();
for(int i = 0; i < size; i++){
int num = que.poll();
res.add(num);
List<Integer> tmp = ngraph.get(num);
for(int j : tmp){
degree[j]--;
if(degree[j] == 0){
que.add(j);
}
}
}
}
Collections.sort(res);
return res;
}
}