这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
802. 找到最终的安全状态
在有向图中,以某个节点为起始节点,从该点出发,每一步沿着图中的一条有向边行走。如果到达的节点是终点(即它没有连出的有向边),则停止。
对于一个起始节点,如果从该节点出发,无论每一步选择沿哪条有向边行走,最后必然在有限步内到达终点,则将该起始节点称作是 安全 的。
返回一个由图中所有安全的起始节点组成的数组作为答案。答案数组中的元素应当按 升序 排列。
该有向图有 n 个节点,按 0 到 n - 1 编号,其中 n 是 graph 的节点数。图以下述形式给出:graph[i] 是编号 j 节点的一个列表,满足 (i, j) 是图的一条有向边。
示例 1:
输入:graph = [[1,2],[2,3],[5],[0],[5],[],[]]
输出:[2,4,5,6]
解释:示意图如上。
示例 2:
输入:graph = [[1,2,3,4],[1,2],[3,4],[0,4],[]]
输出:[4]
提示:
n == graph.length1 <= n <= 1040 <= graph[i].length <= ngraph[i]按严格递增顺序排列。- 图中可能包含自环。
- 图中边的数目在范围
[1, 4 * 104]内。
方法一
DFS:利用深搜,保存当前搜索的路径;如果搜到的点已经在路径中,说明当前路径中的点在环中,不是安全点,记录下来;如果当前搜到的点能够到达在环中的点,则也不是安全点,将其记录下来;最后,没有记录过的就是安全点;
class Solution {
int n, m, idx = 0;
int[] h, ne, e;
boolean[] st;
HashMap<Integer, Integer> res;
LinkedList<Integer> path;
public List<Integer> eventualSafeNodes(int[][] graph) {
n = graph.length;
for (int i = 0; i < graph.length; i ++ ) m += graph[i].length;
init();
for (int i = 0; i < n; i ++ ) h[i] = -1;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < graph[i].length; j ++ )
add(i, graph[i][j]);
for (int i = 0; i < n; i ++ ) {
if (!st[i])
dfs(i);
}
for (int i = 0; i < n; i ++ )
if (!res.containsKey(i))
path.add(i);
Collections.sort(path);
return path;
}
void init() {
h = new int[n];
ne = new int[m];
e = new int[m];
st = new boolean[n];
res = new HashMap<>();
path = new LinkedList<>();
}
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void dfs(int u) {
path.addLast(u);
st[u] = true;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (path.contains(j)) {
for (int x : path)
res.put(x, 1);
continue;
}
if (!st[j]) dfs(j);
if (res.containsKey(j)) {
res.put(u, 1);
}
}
path.removeLast();
}
}
时间复杂度: O(n)
空间复杂度: O(n + m)
方法二
拓扑排序:生成一个与原图逆向的图,利用拓扑排序的方法,将入度为0的点删去,并将其指向的边删去;如果删去的边的另一个顶点此时入度为0,则加入队列;直至没有入度为0的点;最后入度为0的点,即是安全点;
class Solution {
public List<Integer> eventualSafeNodes(int[][] graph) {
int n = graph.length;
int[] d = new int[n];
List<List<Integer>> rev = new ArrayList<>();
for (int i = 0; i < n; i ++ ) {
d[i] = graph[i].length;
rev.add(new ArrayList());
}
for (int i = 0; i < n; i ++ )
for (int j = 0; j < graph[i].length; j ++ )
rev.get(graph[i][j]).add(i);
LinkedList<Integer> q = new LinkedList<>();
for (int i = 0; i < n; i ++ )
if (d[i] == 0)
q.add(i);
while(q.size() != 0) {
int t = q.poll();
for (int i = 0; i < rev.get(t).size(); i ++ )
if (-- d[rev.get(t).get(i)] == 0)
q.add(rev.get(t).get(i));
}
List<Integer> res = new ArrayList<>();
for (int i = 0; i < n; i ++ )
if (d[i] == 0)
res.add(i);
Collections.sort(res);
return res;
}
}
时间复杂度: O(n + m)
空间复杂度: O(n + m)
