题目地址:
https://leetcode.com/problems/find-eventual-safe-states/
给定一个有向图,如果从一个点出发走若干步后总会走到一个出度为 0 0 0的点,则称其为eventual safe。要求返回该图中所有的eventual safe的点。
题目的本质是要求把能走到某个环上的点都去掉不返回,而只返回那些走不到环上的点。一个点能否走到环上,当且仅当其邻居能否走到环上,这是一个递归的问题。我们可以对每个点进行染色, 0 0 0代表还没访问,可以进行访问;染 1 1 1代表本次DFS正在访问,染 2 2 2代表不能走到环上。同时我们让DFS返回是否发现了环,发现了环(表示当前顶点不是eventual safe的)则返回false,否则返回true。开始对每个点进行DFS,如果这个点之前已经染了色了,那说明之前DFS的时候已经判断好了其是否能走到环上,视情况加入最终结果即可;如果没染色(意思是标记为 0 0 0),那就对其进行访问,先将其标记为 1 1 1,然后开始访问其邻居,如果邻居为 2 2 2,说明该邻居走不到环上,略过;如果邻居为 1 1 1,说明产生了环,返回false;如果对邻居进行DFS发现了环,那也说明当前顶点不是eventual safe的,返回false。对其邻居全遍历完后如果都没有返回false,说明当前顶点是eventual safe的,则将其染为 2 2 2,同时返回true。只需要将所有染为 2 2 2的顶点加入最终结果即可,事实上,最后染为 1 1 1的点就是能走到环上的点。代码如下:
import java.util.ArrayList;
import java.util.List;
public class Solution {
public List<Integer> eventualSafeNodes(int[][] graph) {
List<Integer> res = new ArrayList<>();
// 判空
if (graph == null) {
return res;
}
int[] color = new int[graph.length];
for (int i = 0; i < graph.length; ++i) {
if (dfs(i, color, graph)) {
res.add(i);
}
}
return res;
}
// 从node开始DFS,返回node是否是eventual safe的
public boolean dfs(int node, int[] color, int[][] graph) {
// 如果之前访问过,就返回其是否eventual safe
if (color[node] > 0) {
return color[node] == 2;
}
// 标记其为正在访问
color[node] = 1;
// 开始递归遍历其邻居
for (int next: graph[node]) {
// 如果已经知道next不会走到环了,就不必访问了,略过
if (color[next] == 2) {
continue;
}
// 如果next在当前DFS访问过,说明产生了环,返回false;
// 如果next未访问过则对其进行访问,如果发现了环返回false
if (color[next] == 1 || !dfs(next, color, graph)) {
return false;
}
}
// 如果之前都没发现环,则标记为2,返回true
color[node] = 2;
return true;
}
}
时间复杂度 O ( V + E ) O(V+E) O(V+E),空间 O ( V ) O(V) O(V)。