802. 找到最终的安全状态

题目:

思路:

【1】模拟的方式(这种方式虽然能跑,但是会超时)

【2】深度优先搜索 + 三色标记法(这种方式算是 模拟的方式 的优化版)

在深度优先搜索时,用三种颜色对节点进行标记,标记的规则如下:
白色(用 0 表示):该节点尚未被访问;
灰色(用 1 表示):该节点位于递归栈中,或者在某个环上;
黑色(用 2 表示):该节点搜索完毕,是一个安全节点。

 

【3】拓扑排序

将所有入度为 0 的点加入队列,然后不断取出队首元素,
将其出边相连的点的入度减一,若该点入度减一后为 0,则将该点加入队列,如此循环直至队列为空。
循环结束后,所有入度为 0 的节点均为安全的。
我们遍历入度数组,并将入度为 0 的点加入答案列表

 

代码展示:

【1】模拟的方式(这种方式虽然能跑,但是会超时)

class Solution {
    public List<Integer> eventualSafeNodes(int[][] graph) {
        int n = graph.length;
        boolean[] isRes = new boolean[n];
        Set<Integer> terminal = new HashSet<Integer>();
        for (int i = 0; i < n; i++){
            int[] temp = graph[i];
            // 如果出度为0,则是终端,应纳入终端的集合且也算是结果集中的一员
            if (temp.length == 0){
                terminal.add(i);
                isRes[i] = true;
            }
        }
        boolean[] flag = new boolean[n];
        for (int i = 0; i < n; i++){
            flag[i] = true;
            if (!isRes[i] && isAllPathToTerminal(graph,i,terminal,flag)){
                isRes[i] = true;
            }
            flag[i] = false;
        }

        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < n; i++){
            if (isRes[i]) res.add(i);
        }
        return res;
    }

    /**
     * 检测该节点开始的所有可能路径是否都通向终端节点
     * @param graph 路径的图指向
     * @param flag  当前节点下标
     * @param terminal 终端结点集合
     * @param flag 走过路径的标志
     * @return
     */
    public boolean isAllPathToTerminal(int[][] graph,int i,Set<Integer> terminal,boolean[] flag){
        if (terminal.contains(i)) return true;
        boolean res = true;
        for (int data : graph[i]){
            // 如果走重复的位置,则说明有环的存在(不是全路径都通向终端节点)
            if (flag[data] == true) return false;
            flag[data] = true;
            res &= isAllPathToTerminal(graph,data,terminal,flag);
            flag[data] = false;
        }
        return res;
    }
}

【2】深度优先搜索 + 三色标记法

//时间4 ms 击败 100%
//内存53.4 MB 击败 33.80%
//时间复杂度:O(n+m),其中 n 是图中的点数,m 是图中的边数。
//空间复杂度:O(n)。存储节点颜色以及递归栈的开销均为 O(n)。
class Solution {
    public List<Integer> eventualSafeNodes(int[][] graph) {
        int n = graph.length;
        int[] color = new int[n];
        List<Integer> ans = new ArrayList<Integer>();
        for (int i = 0; i < n; ++i) {
            if (safe(graph, color, i)) {
                ans.add(i);
            }
        }
        return ans;
    }

    /**
     * 判断该节点是否处于安全节点
     * @param graph 路径的图指向
     * @param color 记录遍历节点的颜色
     * 白色(用 0 表示):该节点尚未被访问;
     * 灰色(用 1 表示):该节点位于递归栈中,或者在某个环上;
     * 黑色(用 2 表示):该节点搜索完毕,是一个安全节点。
     * @param x 当前节点的下标
     * @return
     */
    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;
    }
}

【3】拓扑排序

//时间23 ms 击败 21.66%
//内存54.3 MB 击败 5.52%
//时间复杂度:O(n+m),其中 n 是图中的点数,m 是图中的边数。
//空间复杂度:O(n+m)。需要 O(n+m) 的空间记录反图。
class Solution {
    public List<Integer> eventualSafeNodes(int[][] graph) {
        int n = graph.length;
        List<List<Integer>> rg = new ArrayList<List<Integer>>();
        for (int i = 0; i < n; ++i) {
            rg.add(new ArrayList<Integer>());
        }
        int[] inDeg = new int[n];
        for (int x = 0; x < n; ++x) {
            for (int y : graph[x]) {
                rg.get(y).add(x);
            }
            inDeg[x] = graph[x].length;
        }

        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < n; ++i) {
            if (inDeg[i] == 0) {
                queue.offer(i);
            }
        }
        while (!queue.isEmpty()) {
            int y = queue.poll();
            for (int x : rg.get(y)) {
                if (--inDeg[x] == 0) {
                    queue.offer(x);
                }
            }
        }

        List<Integer> ans = new ArrayList<Integer>();
        for (int i = 0; i < n; ++i) {
            if (inDeg[i] == 0) {
                ans.add(i);
            }
        }
        return ans;
    }
}

 

posted @ 2023-07-25 14:50  忧愁的chafry  阅读(21)  评论(0编辑  收藏  举报