找到最终的安全状态

在一个有向图中,我们从某个节点开始,每次沿着图的有向边走。 如果我们到达一个终端节点(也就是说,它没有指向外面的边),就停止。

现在,如果说我们的起始节点“最终是安全的”,当且仅当我们最终可以走到终端节点。 更具体地说,存在自然数K,对于任何行走的路线,我们必须在少于K步的情况下停在终端节点。

哪些节点是最终安全的? 返回它们升序排列的数组。

有向图具有N个节点,其标签为0, 1, ..., N-1,其中N是图的长度。 该图以下面的形式给出: graph[i]是从i出发,通过边(i, j),所有能够到达的节点j组成的链表。

 

 

 

find-eventual-safe-states

 

样例

样例1

输入: [[1,2],[2,3],[5],[0],[5],[],[]]
输出: [2,4,5,6]

 

样例2

输入: [[4,9],[3,5,7],[0,3,4,5,6,8],[7,8,9],[5,6,7,8],[6,7,8,9],[7,9],[8,9],[9],[]]
输出: [0,1,2,3,4,5,6,7,8,9]

 

 

分析
这道题给了我们一个有向图,然后定义了一种最终安全状态的结点,就是说该结点要在自然数K步内停止,所谓停止的意思,就是再没有向外的边,即没有出度,像上面例子中的结点5和6就是出度为0,因为graph[5]和graph[6]均为空。那么我们分析题目中的例子,除了没有出度的结点5和6之外,结点2和4也是安全状态结点,为啥呢,我们发现结点2和4都只能到达结点5,而结点5本身就是安全状态点,所以2和4也就是安全状态点了,所以我们可以得出的结论是,若某结点唯一能到达的结点是安全状态结点的话,那么该结点也同样是安全状态结点。那么我们就可以从没有出度的安全状态往回推,比如结点5,往回推可以到达结点4和2,先看结点4,此时我们先回推到结点4,然后将这条边断开,那么此时结点4出度为0,则标记结点4也为安全状态结点,同理,回推到结点2,断开边,此时结点2虽然入度仍为2,但是出度为0了,标记结点2也为安全状态结点
​

 

​
思路1、DFS
1、使用 dfs 遍历有向图的解法,经过分析某些结点不是安全状态,因为有环的存在,而环经过的所有结点,一定不是安全状态结点,所以我们可以通过DFS遍历有向图来找出环即可。在大多数的算法中,经典的DFS遍历法对于结点都有三种状态标记 UNKNOWN(0) SAFE(1) UNSAFE(-1),如果当前节点不是 UNKNOWN 的,表示改节点已经被访问过了,如果当前节点是 SAFE 返回 true 否则返回 false 。
2、然后开始遍历所有邻接结点,对该邻结点调用递归返回false了,说明当前结点是环结点,返回false。
​
思路2、BFS
1、遍历数组,使用 Map 记录每个节点的出度,只需先找到出度为 0 的数据存入 Queue
2、循环非空 Queue,取出队首元素,map包含改key,遍历 set 判断改节点是否存在,存在出度减 -1,存入 Queue
3、最后遍历数组indegree 如果出度为0 存入集合即可
​
​
​
public class Solution {
  
    private static final int UNKNOWN = 0;
    private static final int UNSAFE = -1;
    private static final int SAFE = 1;
​
    /**
     * @param graph: a 2D integers array
     * @return: return a list of integers
     */
    public List<Integer> eventualSafeNodes(int[][] graph) {
        // write your code here
        int len = graph.length;
        int[] states = new int[len];
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            if (helper(i, graph, states)) {
                result.add(i);
            }
        }
        return result;
​
    }
​
    private boolean helper(int index, int[][] graph, int[] states) {
        if (states[index] != UNKNOWN) {
            return states[index] == SAFE;
        }
        states[index] = UNSAFE;
        for (int node : graph[index]) {
            if (!helper(node, graph, states)) {
                return false;
            }
        }
        states[index] = SAFE;
        return true;
    }
​
}
​
public class Solution {
    /**
     * @param graph: a 2D integers array
     * @return: return a list of integers
     */
    public List<Integer> eventualSafeNodes(int[][] graph) {
        // write your code here
        int len = graph.length;
        int[]  = new int[len];
        Map<Integer, Set<Integer>> map = new HashMap<>();
        Queue<Integer> queue = new LinkedList<>();
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            int[] item = graph[i];
            for (int edge : item) {
                if (!map.containsKey(edge)) {
                    map.put(edge, new HashSet<>());
                }
                map.get(edge).add(i);
            }
            indegree[i] = item.length;
            if (indegree[i] == 0) {
                queue.offer(i);
            }
        }
        while (!queue.isEmpty()) {
            Integer node = queue.poll();
            if (map.containsKey(node)) {
                Set<Integer> set = map.get(node);
                for (Integer i : set) {
                    indegree[i]--;
                    if (indegree[i] == 0) {
                        queue.offer(i);
                    }
                }
            }
        }
​
        for (int i = 0; i < len; i++) {
            if (indegree[i] == 0) {
                result.add(i);
            }
        }
        Collections.sort(result);
        return result;
    }
}

 

源码地址:https://github.com/xingfu0809/Java-LintCode 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值