当前位置:   article > 正文

JAVA LeetCode#802 找到最终的安全状态_802找出最终的安全状态 java

802找出最终的安全状态 java

题目描述

在有向图中, 我们从某个节点和每个转向处开始, 沿着图的有向边走。 如果我们到达的节点是终点 (即它没有连出的有向边), 我们停止。

现在, 如果我们最后能走到终点,那么我们的起始节点是最终安全的。 更具体地说, 存在一个自然数 K,  无论选择从哪里开始行走, 我们走了不到 K 步后必能停止在一个终点。

哪些节点最终是安全的? 结果返回一个有序的数组。

该有向图有 N 个节点,标签为 0, 1, ..., N-1, 其中 N 是 graph 的节点数.  图以以下的形式给出: graph[i] 是节点 j 的一个列表,满足 (i, j) 是图的一条有向边。

示例:
输入:graph = [[1,2],[2,3],[5],[0],[5],[],[]]
输出:[2,4,5,6]
这里是上图的示意图。

提示:


    graph 节点数不超过 10000.
    图的边数不会超过 32000.
    每个 graph[i] 被排序为不同的整数列表, 在区间 [0, graph.length - 1] 中选取。

来源:力扣(LeetCode
链接:https://leetcode-cn.com/problems/find-eventual-safe-states

 

 

思路过程

这题一共提交了5次,慢慢修改,最后12ms,在所有 Java 提交中击败了100.00% 的用户!

 

第一次提交:
这题最先是考虑如果形成环的话就不加入,用flag[i]进行标记是否已经访问,如果重复访问说明成环,不加入。嗯,测试没问题,提交,结果超时了

  1. public static List<Integer> eventualSafeNodes(int[][] graph) {
  2. List<Integer> ans = new ArrayList<Integer>();//记录结果
  3. int n = graph.length;
  4. boolean[] flag = new boolean[n];
  5. for ( int i = 0; i < n; i++ ) {
  6. if (!DFS(graph, i, flag)) ans.add(i);//DFS遍历
  7. Arrays.fill(flag, false);//复位
  8. }
  9. return ans;
  10. }
  11. public static boolean DFS( int[][] graph, int start, boolean[] flag ) {
  12. if ( flag[start] ) return true;//循环
  13. flag[start] = true;
  14. for ( int next : graph[start] ) {
  15. if ( DFS(graph, next, flag) ) return true;//发生循环则返回
  16. }
  17. flag[start] = false;//回退
  18. return false;
  19. }

第二次提交:
额。。。思考了一下,每次都得遍历环时间复杂度很高,于是加了个set记录已经是环的点,这样就不用重复遍历环,如果遍历到该点,则直接返回,提交,又超时。。。继续改!!!

第三次提交:
把安全点也用set记录,提交,哇,终于过了。362 ms, 在所有 Java 提交中击败了6.67% 的用户。。。额。。这么渣的吗

  1. public static List<Integer> eventualSafeNodes(int[][] graph) {
  2. Set<Integer> set = new HashSet<Integer>();//标记成环的点
  3. Set<Integer> f = new HashSet<Integer>();//标记安全的点
  4. List<Integer> ans = new ArrayList<Integer>();//记录结果
  5. int n = graph.length;
  6. boolean[] flag = new boolean[n];
  7. for ( int i = 0; i < n; i++ ) {
  8. if (!DFS(graph, i, flag, set, f)) ans.add(i);//DFS遍历
  9. else set.add(i);
  10. Arrays.fill(flag, false);
  11. }
  12. return ans;
  13. }
  14. public static boolean DFS( int[][] graph, int start, boolean[] flag, Set set, Set f ) {
  15. if ( f.contains(start) ) return false;//如果是安全点
  16. if ( flag[start] || set.contains(start) ) return true;//以访问或是成环的点
  17. flag[start] = true;//标记以访问
  18. for ( int next : graph[start] ) {
  19. if ( DFS(graph, next, flag, set, f) ) {
  20. set.add(start);//成环
  21. return true;
  22. }
  23. }
  24. flag[start] = false;
  25. f.add(start);//不成环
  26. return false;
  27. }

第四次提交:
看一下网上解题吧,用的拓扑排序?嗯,看一下资料,继续改,提交!77 ms, 在所有 Java 提交中击败了43.33% 的用户。。。
这不是正确解法?!什么鬼

  1. public static List<Integer> eventualSafeNodes(int[][] graph) {
  2. LinkedList<Integer> Q = new LinkedList<Integer>(); //队列
  3. List<Integer> ans = new ArrayList<Integer>(); //存储结果
  4. ArrayList<ArrayList<Integer>> arrs = new ArrayList<ArrayList<Integer>>();//反向记录
  5. int n = graph.length;
  6. int[] out = new int[n];
  7. for ( int i = 0; i < n; i++ ) arrs.add(new ArrayList<Integer>());
  8. for ( int i = 0; i < graph.length; i++ ) {
  9. out[i] += graph[i].length;//记录出度个数
  10. if ( out[i] == 0 ) Q.add(i); //加入队列
  11. for ( int j = 0; j < graph[i].length; j++ ) {
  12. arrs.get(graph[i][j]).add(i);
  13. }
  14. }
  15. while ( !Q.isEmpty() ) {
  16. int index = Q.get(0); //出队
  17. Q.remove(0);
  18. for (int i : arrs.get(index)) {
  19. if ( --out[i] == 0 ) Q.add(i); //减去出度,入队
  20. }
  21. }
  22. for ( int i = 0; i < out.length; i++ ) {
  23. if ( out[i] == 0 ) ans.add(i);//安全点
  24. }
  25. return ans;
  26. }

复制其他代码,提交!15ms(╯‵□′)╯︵┻━┻

第五次提交:
仔细一看,这代码怎么跟我原先的思路差不多,研究研究,这不就是一模一样吗?只不过使用得很巧妙啊,直接用数组进行标记,嗯,再写一遍,提交,12 ms, 在所有 Java 提交中击败了100.00% 的用户,我的天啊,第一次啊

  1. /*
  2. * 0:未访问
  3. * 1:以访问
  4. * 2:安全
  5. * 3:成环
  6. */
  7. public static List<Integer> eventualSafeNodes(int[][] graph) {
  8. List<Integer> ans = new ArrayList<Integer>();//记录结果
  9. int n = graph.length;//长度
  10. int[] type = new int[n];//访问类型
  11. for ( int i = 0; i < n; i++ ) {
  12. if ( DFS(graph, i, type) == 2 ) ans.add(i);
  13. }
  14. return ans;
  15. }
  16. public static int DFS( int[][] graph, int index, int[] type ) {
  17. if ( type[index] == 1 ) return 3;//如果访问过了,说明成环
  18. if ( type[index] != 0 ) return type[index]; //如果不是0,返回自身
  19. type[index] = 1;//标记访问了
  20. for (int i : graph[index]) {
  21. if ( DFS(graph, i, type) == 3 ) {
  22. type[i] = 3;
  23. return 3;
  24. }
  25. }
  26. type[index] = 2;//不成环
  27. return 2;
  28. }

总结:难得的一次对题目进行深入研究,关键在于研究的过程!发现超时,那么用set记录下来,减少不必要的遍历,362ms。看题解,自己写一遍,77ms,不满意这个速度,继续研究。发现自己的思路还能这样使用,少了初始化flag,也节约了空间!12ms。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/995289
推荐阅读
相关标签
  

闽ICP备14008679号