LeetCode每日一题 802.找到最终的安全状态

165 阅读1分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

802. 找到最终的安全状态

在有向图中,以某个节点为起始节点,从该点出发,每一步沿着图中的一条有向边行走。如果到达的节点是终点(即它没有连出的有向边),则停止。

对于一个起始节点,如果从该节点出发,无论每一步选择沿哪条有向边行走,最后必然在有限步内到达终点,则将该起始节点称作是 安全 的。

返回一个由图中所有安全的起始节点组成的数组作为答案。答案数组中的元素应当按 升序 排列。

该有向图有 n 个节点,按 0n - 1 编号,其中 ngraph 的节点数。图以下述形式给出:graph[i] 是编号 j 节点的一个列表,满足 (i, j) 是图的一条有向边。

示例 1:

Illustration of graph

输入: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 * 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)

image-20210805093554397

方法二

拓扑排序:生成一个与原图逆向的图,利用拓扑排序的方法,将入度为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)

image-20210805101320430