赞
踩
在有向图中, 我们从某个节点和每个转向处开始, 沿着图的有向边走。 如果我们到达的节点是终点 (即它没有连出的有向边), 我们停止。
现在, 如果我们最后能走到终点,那么我们的起始节点是最终安全的。 更具体地说, 存在一个自然数 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]进行标记是否已经访问,如果重复访问说明成环,不加入。嗯,测试没问题,提交,结果超时了
- public static List<Integer> eventualSafeNodes(int[][] graph) {
- List<Integer> ans = new ArrayList<Integer>();//记录结果
- int n = graph.length;
- boolean[] flag = new boolean[n];
- for ( int i = 0; i < n; i++ ) {
- if (!DFS(graph, i, flag)) ans.add(i);//DFS遍历
- Arrays.fill(flag, false);//复位
- }
- return ans;
- }
-
- public static boolean DFS( int[][] graph, int start, boolean[] flag ) {
- if ( flag[start] ) return true;//循环
- flag[start] = true;
- for ( int next : graph[start] ) {
- if ( DFS(graph, next, flag) ) return true;//发生循环则返回
- }
- flag[start] = false;//回退
- return false;
- }
第二次提交:
额。。。思考了一下,每次都得遍历环时间复杂度很高,于是加了个set记录已经是环的点,这样就不用重复遍历环,如果遍历到该点,则直接返回,提交,又超时。。。继续改!!!
第三次提交:
把安全点也用set记录,提交,哇,终于过了。362 ms, 在所有 Java 提交中击败了6.67% 的用户。。。额。。这么渣的吗
- public static List<Integer> eventualSafeNodes(int[][] graph) {
- Set<Integer> set = new HashSet<Integer>();//标记成环的点
- Set<Integer> f = new HashSet<Integer>();//标记安全的点
- List<Integer> ans = new ArrayList<Integer>();//记录结果
- int n = graph.length;
- boolean[] flag = new boolean[n];
- for ( int i = 0; i < n; i++ ) {
- if (!DFS(graph, i, flag, set, f)) ans.add(i);//DFS遍历
- else set.add(i);
- Arrays.fill(flag, false);
- }
- return ans;
- }
-
- public static boolean DFS( int[][] graph, int start, boolean[] flag, Set set, Set f ) {
- if ( f.contains(start) ) return false;//如果是安全点
- if ( flag[start] || set.contains(start) ) return true;//以访问或是成环的点
- flag[start] = true;//标记以访问
- for ( int next : graph[start] ) {
- if ( DFS(graph, next, flag, set, f) ) {
- set.add(start);//成环
- return true;
- }
- }
- flag[start] = false;
- f.add(start);//不成环
- return false;
- }
第四次提交:
看一下网上解题吧,用的拓扑排序?嗯,看一下资料,继续改,提交!77 ms, 在所有 Java 提交中击败了43.33% 的用户。。。
这不是正确解法?!什么鬼
- public static List<Integer> eventualSafeNodes(int[][] graph) {
- LinkedList<Integer> Q = new LinkedList<Integer>(); //队列
- List<Integer> ans = new ArrayList<Integer>(); //存储结果
- ArrayList<ArrayList<Integer>> arrs = new ArrayList<ArrayList<Integer>>();//反向记录
- int n = graph.length;
- int[] out = new int[n];
- for ( int i = 0; i < n; i++ ) arrs.add(new ArrayList<Integer>());
- for ( int i = 0; i < graph.length; i++ ) {
- out[i] += graph[i].length;//记录出度个数
- if ( out[i] == 0 ) Q.add(i); //加入队列
- for ( int j = 0; j < graph[i].length; j++ ) {
- arrs.get(graph[i][j]).add(i);
- }
- }
-
- while ( !Q.isEmpty() ) {
- int index = Q.get(0); //出队
- Q.remove(0);
-
- for (int i : arrs.get(index)) {
- if ( --out[i] == 0 ) Q.add(i); //减去出度,入队
- }
- }
-
- for ( int i = 0; i < out.length; i++ ) {
- if ( out[i] == 0 ) ans.add(i);//安全点
- }
- return ans;
- }
复制其他代码,提交!15ms(╯‵□′)╯︵┻━┻
第五次提交:
仔细一看,这代码怎么跟我原先的思路差不多,研究研究,这不就是一模一样吗?只不过使用得很巧妙啊,直接用数组进行标记,嗯,再写一遍,提交,12 ms, 在所有 Java 提交中击败了100.00% 的用户,我的天啊,第一次啊
- /*
- * 0:未访问
- * 1:以访问
- * 2:安全
- * 3:成环
- */
- public static List<Integer> eventualSafeNodes(int[][] graph) {
- List<Integer> ans = new ArrayList<Integer>();//记录结果
- int n = graph.length;//长度
- int[] type = new int[n];//访问类型
- for ( int i = 0; i < n; i++ ) {
- if ( DFS(graph, i, type) == 2 ) ans.add(i);
- }
- return ans;
- }
-
- public static int DFS( int[][] graph, int index, int[] type ) {
- if ( type[index] == 1 ) return 3;//如果访问过了,说明成环
- if ( type[index] != 0 ) return type[index]; //如果不是0,返回自身
- type[index] = 1;//标记访问了
- for (int i : graph[index]) {
- if ( DFS(graph, i, type) == 3 ) {
- type[i] = 3;
- return 3;
- }
- }
-
- type[index] = 2;//不成环
- return 2;
- }
总结:难得的一次对题目进行深入研究,关键在于研究的过程!发现超时,那么用set记录下来,减少不必要的遍历,362ms。看题解,自己写一遍,77ms,不满意这个速度,继续研究。发现自己的思路还能这样使用,少了初始化flag,也节约了空间!12ms。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。