In a directed graph, we start at some node and every turn, walk along a directed edge of the graph.  If we reach a node that is terminal (that is, it has no outgoing directed edges), we stop.

Now, say our starting node is eventually safe if and only if we must eventually walk to a terminal node.  More specifically, there exists a natural number K so that for any choice of where to walk, we must have stopped at a terminal node in less than K steps.

Which nodes are eventually safe?  Return them as an array in sorted order.

The directed graph has N nodes with labels 0, 1, ..., N-1, where N is the length of graph.  The graph is given in the following form: graph[i] is a list of labels j such that (i, j) is a directed edge of the graph.

Example:
Input: graph = [[1,2],[2,3],[5],[0],[5],[],[]]
Output: [2,4,5,6]
Here is a diagram of the above graph.

Note:

  • graph will have length at most 10000.
  • The number of edges in the graph will not exceed 32000.
  • Each graph[i] will be a sorted list of different integers, chosen within the range [0, graph.length - 1].

这道题给了我们一个有向图,然后定义了一种最终安全状态的结点,就是说该结点要在自然数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也为安全状态结点。

分析到这里,思路应该比较明朗了,由于我们需要回推边,所以需要建立逆向边,用一个集合数组来存,由于题目要求返回的结点有序,我们可以利用集合TreeSet的自动排序的特性,由于需要断开边,为了不修改输入数据,所以我们干脆再建一个顺向边得了,即跟输入数据相同。还需要一个safe数组,布尔型的,来标记哪些结点是安全状态结点。在遍历结点的时候,直接先将出度为0的安全状态结点找出来,排入一个队列queue中,方便后续的处理。后续的处理就有些类似BFS的操作了,我们循环非空queue,取出队首元素,标记safe中该结点为安全状态结点,然后遍历其逆向边的结点,即可以到达当前队首结点的所有结点,我们在正向边集合中删除对应的边,如果此时结点出度为0了,将其加入队列queue中等待下一步处理,这样while循环退出后,所有的安全状态结点都已经标记好了,我们直接遍历safe数组,将其存入结果res中即可,参见代码如下:

解法一:

class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
vector<int> res;
int n = graph.size();
vector<bool> safe(n, false);
vector<set<int>> g(n, set<int>()), revg = g;
queue<int> q;
for (int i = ; i < n; ++i) {
if (graph[i].empty()) q.push(i);
for (int j : graph[i]) {
g[i].insert(j);
revg[j].insert(i);
}
}
while (!q.empty()) {
auto t = q.front(); q.pop();
safe[t] = true;
for (int i : revg[t]) {
g[i].erase(t);
if (g[i].empty()) q.push(i);
}
}
for (int i = ; i < n; ++i) {
if (safe[i]) res.push_back(i);
}
return res;
}
};

我们再来看一种DFS遍历有向图的解法。仔细分析题目中的例子,不难发现,之所以某些结点不是安全状态,因为有环的存在,而环经过的所有结点,一定不是安全状态结点,所以我们可以通过DFS遍历有向图来找出环即可。在大多数的算法中,经典的DFS遍历法对于结点都有三种状态标记,white,gray,和black,其中white表示结点还未遍历,gray表示正在遍历邻结点,black表示已经结束该结点的遍历。那么我们可以对每个结点都调用递归函数,在递归函数中,如果当前结点不是white,表示该结点已经访问过了,那么如果当前结点是black,直接返回true,如果是gray,直接返回false,因为遇到gray的结点,表示一定有环存在。否则我们给结点标记gray,然后开始遍历所有邻接结点,如果某个邻结点是black,直接跳过该结点。如果某个邻结点是gray,或者对该邻结点调用递归返回false了,说明当前结点是环结点,返回false。如果循环结束了,当前结点标记为black,并且返回true,参见代码如下:

解法二:

class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
int n = graph.size();
vector<int> res, color(n); // 0 white, 1 gray, 2 black
for (int i = ; i < n; ++i) {
if (helper(graph, i, color)) res.push_back(i);
}
return res;
}
bool helper(vector<vector<int>>& graph, int cur, vector<int>& color) {
if (color[cur] > ) return color[cur] == ;
color[cur] = ;
for (int i : graph[cur]) {
if (color[i] == ) continue;
if (color[i] == || !helper(graph, i, color)) {
return false;
}
}
color[cur] = ;
return true;
}
};

参考资料:

https://leetcode.com/problems/find-eventual-safe-states/solution/

https://leetcode.com/problems/find-eventual-safe-states/discuss/119871/Straightforward-Java-solution-easy-to-understand!

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Find Eventual Safe States 找到最终的安全状态的更多相关文章

  1. [LeetCode] 802. Find Eventual Safe States 找到最终的安全状态

    In a directed graph, we start at some node and every turn, walk along a directed edge of the graph.  ...

  2. 【LeetCode】802. Find Eventual Safe States 解题报告(Python)

    [LeetCode]802. Find Eventual Safe States 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemi ...

  3. LC 802. Find Eventual Safe States

    In a directed graph, we start at some node and every turn, walk along a directed edge of the graph.  ...

  4. Java实现 LeetCode 802 找到最终的安全状态 (DFS)

    802. 找到最终的安全状态 在有向图中, 我们从某个节点和每个转向处开始, 沿着图的有向边走. 如果我们到达的节点是终点 (即它没有连出的有向边), 我们停止. 现在, 如果我们最后能走到终点,那么 ...

  5. [Swift]LeetCode802. 找到最终的安全状态 | Find Eventual Safe States

    In a directed graph, we start at some node and every turn, walk along a directed edge of the graph.  ...

  6. 【leetcode】802. Find Eventual Safe States

    题目如下: 解题思路:本题大多数人采用DFS的方法,这里我用的是另一种方法.我的思路是建立一次初始值为空的safe数组,然后遍历graph,找到graph[i]中所有元素都在safe中的元素,把i加入 ...

  7. LeetCode 802. Find Eventual Safe States

    原题链接在这里:https://leetcode.com/problems/find-eventual-safe-states/ 题目: In a directed graph, we start a ...

  8. 802. Find Eventual Safe States

    https://leetcode.com/problems/find-eventual-safe-states/description/ class Solution { public: vector ...

  9. [LeetCode] Cracking the Safe 破解密码

    There is a box protected by a password. The password is n digits, where each letter can be one of th ...

随机推荐

  1. dubbo和dubboX与微服务架构(dubbo一)

    一.传统三层架构模式的缺陷 三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)web.业务逻辑层(Bu ...

  2. vue-resource的使用,前后端数据交互

    vue-resource的使用,前后端数据交互 1:导入vue与vue-resource的js js下载:   https://pan.baidu.com/s/1fs5QaNwcl2AMEyp_kUg ...

  3. MyBatis使用注意事项

    目录 1. 使用何种映射器配置 2. 对象生命周期和作用域 SqlSessionFactoryBuilder SqlSessionFactory SqlSession 映射器实例(Mapper Ins ...

  4. MySQL无法插入中文的解决方案

    本人在做数据库的连接过程中,发现无法插入中文值.原因是mysql的默认编码是latin1,只须将编码改为utf8即可. 在mysql的命令行窗口中输入 status 会出现当前的编码.在mysql的安 ...

  5. 【汇总目录】Git

    基础教程 [2019年03月26日] 推送提交(git push) [2019年03月26日] 远程分支 [2019年03月26日] 基本的合并冲突处理 [2019年03月25日] 基本的分支与合并操 ...

  6. SQLserver 数据库高版本无法还原到低版本的数据解决方法

    sql server 数据库的版本只支持从上往下兼容.即高版本可以兼容低版本 .低版本不能兼容低版本.通常我们在开发时会用比较高的版本.但是部署到客户那边可能他们的数据库版本会比较低. 我们可以通过导 ...

  7. struts2简单入门-数据校验

    数据校验流程 校验数据的方式 重写execute方法在内部写校验代码 public class LoginAdminAction extends ActionSupport { private Use ...

  8. JAVA进阶11

    间歇性混吃等死,持续性踌躇满志系列-------------第11天 1.ArrayList package cn.intcast.demo11; import java.util.ArrayList ...

  9. 网络学习day04_VLSM、子网划分

    IP子网划分 首先,在进行子网划分的学习之前,我们先来回顾一下IP地址的相关知识,同时了解一下公有和私有IP地址: 在Internet上有千百万台主机,为了区分这些主机,人们给每台主机都分配了一个专门 ...

  10. Mysql中设置指定IP的特定用户及特定权限

    创建用户:格式:grant select on 数据库.* to 用户名@登录主机 identified by '密码' 举例: 例 1:增加一个用户 test1 密码为 abc,让他可以在任何主机上 ...