一、问题描述
有向图中,从起始节点出发,每一步沿图的一有向边。如果到达的节点是终点(即其没出的有向边),则停止。
若从起始节点出发,无论每一步选择沿哪条有向边行走,最后必在有限步到终点,则将该节点是 安全节点 。
返回图中所有安全节点组成的升序数组。
该有向图有 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.length
1 <= n <= 104
0 <= graph[i].length <= n
graph[i] 按严格递增顺序排列。
图中可能包含自环。
图中边的数目在范围 [1, 4 * 10^4] 内。
三、解题思路
第一次采用暴力 dfs 破解( 超过时间限制 ):由于不成环的节点遍历最多为 n -1 次,深度到 n 确定成环不符合要求,若访问到 nums[x].length == 0,直接返回当前深度,已经遍历完毕,为安全节点。获取终点列表中的最大深度,大于或等于 n 则不符合要求。
采用 深度优先遍历 + 三色标记法 :判断是否成环为解题关键
利用 int[] color = new int[length];
创建节点访问关系数组
- 白色(用 0 表示):未被访问的节点;
- 灰色(用 1 表示):正在访问该节点,或者该节点在某个环上;
- 黑色(用 2 表示):安全节点。
判断节点是否安全(是否成环):
- 初始化所有节点都为白色(未访问)。
- 依次遍历以 x 节点为起点的终点 y,采用 递归 形式深度优先遍历。
- 若访问到灰色节点(在递归栈中的节点)则返回 false ,成环
- 若访问到黑色节点(安全节点)直接返回 true ,以后都不会成环
- 访问时将 x 节点设置为灰色(表明正在访问)
- 访问结束后将 x 节点设置为黑色(安全节点)
//判断节点是否安全
public boolean safe(int[][] graph,int[] color,int x){
if(color[x]>0){
return color[x]==2;
}
color[x] = 1;
for(int y : graph[x]){
if(!safe(graph,color,y)){
return false;
}
}
color[x] = 2;
return true;
}
四、java实现
class Solution {
public List<Integer> eventualSafeNodes(int[][] graph) {
int length = graph.length;
//0--白--未被访问过 1--灰--位于递归栈中 2--黑--搜索完毕,安全结点
int[] color = new int[length];
List<Integer> list = new ArrayList<>();
for(int i=0;i<length;i++){
if(safe(graph,color,i)){
list.add(i);
}
}
return list;
}
public boolean safe(int[][] graph,int[] color,int x){
if(color[x]>0){
return color[x]==2;
}
color[x] = 1;
for(int y : graph[x]){
if(!safe(graph,color,y)){
return false;
}
}
color[x] = 2;
return true;
}
}