力扣 802. 找到最终的安全状态

本文介绍了如何利用深度优先搜索(DFS)和三色标记法解决LeetCode问题,通过判断节点是否可达安全节点,同时探讨了使用拓扑排序简化查找过程。两种方法分别适用于不同场景,帮助理解节点安全性的判断与图论算法的应用。
摘要由CSDN通过智能技术生成

题目来源:https://leetcode-cn.com/problems/find-eventual-safe-states/

大致题意:
给定一个有向图,对于一个节点来说,如果它可以沿着任意边都走向一个没有出边的节点,则其为安全节点。
求出所有的安全节点,按节点值大小返回。

思路

DFS+三色标记法

将节点做标记,分成三种类别:

  1. 未访问节点
  2. 已访问节点
  3. 安全节点

使用DFS对节点进行遍历,使用一个检查函数对节点遍历和检查。对于要遍历的节点,有:

  1. 若当前节点已经访问过,则其要么为安全节点,返回true;要么就不是安全节点,直接返回false。
  2. 标记当前节点为已访问
  3. DFS遍历,在遍历过程中对节点进行检查,若返回false,则当前节点为非安全节点,直接返回false。
  4. 若遍历结束都未返回false,则当前节点为安全节点,标记并返回true。

代码:

public List<Integer> eventualSafeNodes(int[][] graph) {
        int n = graph.length;
        // 存储节点状态
        // 0代表未访问过,1代表已访问过,2代表为安全节点
        int[] state = new int[n]; 
        List<Integer> ans = new ArrayList<Integer>();
        // 从头到尾检查
        for (int i = 0; i < n; i++) {
            if (safeNodeCheck(graph, state, i)) {
                ans.add(i);
            }
        }

        return ans;
    }

    public boolean safeNodeCheck(int[][] graph, int[] state, int node) {
        // 若当前节点已经访问过,则其要么为安全节点,要么已经访问过但不是安全节点
        if (state[node] > 0) {
            return state[node] == 2;
        }
        // 标记
        state[node] = 1;
        // 深度优先遍历节点
        for (int target : graph[node]) {
            // 若目标节点不为安全节点,则当前节点不会为安全节点
            if (!safeNodeCheck(graph, state, target)) {
                return false;
            }
        }
        // 若其并未标记为非安全节点,则定为安全节点
        state[node] = 2;
        return true;
    }
拓扑排序

对于一个节点来说,如果它可以沿着任意边都走向一个没有出边的节点,则其为安全节点。

由题意可知,出度为0的节点定然为安全节点。
那么可以将图反向,则可用拓扑排序求安全节点。可知非安全节点定然是在环内,那么用拓扑排序的思想,所有在拓扑排序之后入度为0(代表其不会入环,也就是所有的边都走向安全节点)的节点定然是安全节点。

代码:

public List<Integer> eventualSafeNodes(int[][] graph) {
        List<List<Integer>> reverseGraph = new ArrayList<List<Integer>>(); // 反向图
        int n = graph.length;
        int[] indegree = new int[n]; // 入度数组
        for (int i = 0; i < n; i++) { // 初始化
            reverseGraph.add(new ArrayList<Integer>());
        }
        // 构建反向图
        for (int node = 0; node < n; node++) {
            for (int target : graph[node]) {
                reverseGraph.get(target).add(node);
            }
            // 存储入度
            indegree[node] = graph[node].length;
        }

        Queue<Integer> queue = new LinkedList<Integer>();
        // 初始时,先加入原图中出度为0的节点进队列
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                queue.offer(i);
            }
        }
        while (!queue.isEmpty()) {
            int node = queue.poll();
            for (int target : reverseGraph.add(node)) {
                // 若排除该入边后,入度为0,则加入队列
                if (-- indegree[target] == 0) {
                    queue.offer(target);
                }
            }
        }
        List<Integer> ans = new ArrayList<Integer>();
        // 所有入度为0的节点即为安全节点
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                ans.add(i);
            }
        }
        return ans;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值