【LeetCode每日一题】——802.找到最终的安全状态

文章目录

一【题目类别】

二【题目难度】

  • 中等

三【题目编号】

  • 802.找到最终的安全状态

四【题目描述】

  • 有一个有 n 个节点的有向图,节点按 0n - 1 编号。图由一个 索引从 0 开始 的 2D 整数数组 graph表示, graph[i]是与节点 i 相邻的节点的整数数组,这意味着从节点 igraph[i]中的每个节点都有一条边。
  • 如果一个节点没有连出的有向边,则该节点是 终端节点 。如果从该节点开始的所有可能路径都通向 终端节点 ,则该节点为 安全节点
  • 返回一个由图中所有 安全节点 组成的数组作为答案。答案数组中的元素应当按 升序 排列。

五【题目示例】

  • 示例 1

    • 输入:graph = \[1,2,2,3,5,0,5,\[\],\[\]]
    • 输出2,4,5,6
    • 解释 :示意图如上。
      • 节点 5 和节点 6 是终端节点,因为它们都没有出边。
      • 从节点 2、4、5 和 6 开始的所有路径都指向节点 5 或 6 。
  • 示例 2

    • 输入:graph = \[1,2,3,4,1,2,3,4,0,4,\[\]]
    • 输出4
    • 解释 :
      • 只有节点 4 是终端节点,从节点 4 开始的所有路径都通向节点 4 。

六【题目提示】

  • n = = g r a p h . l e n g t h n == graph.length n==graph.length
  • 1 < = n < = 1 0 4 1 <= n <= 10^4 1<=n<=104
  • 0 < = g r a p h i . l e n g t h < = n 0 <= graphi.length <= n 0<=graphi.length<=n
  • 0 < = g r a p h i j < = n − 1 0 <= graphij <= n - 1 0<=graphij<=n−1
  • g r a p h i graphi graphi 按严格递增顺序排列。
  • 图中可能包含自环。
  • 图中边的数目在范围 1 , 4 ∗ 1 0 4 1, 4 \* 10\^4 1,4∗104 内。

七【解题思路】

  • 利用拓扑排序的思想解决该问题
  • 我们首先构建一个反向图,即假如之前i -> j,那么反向图就变为j -> i,反向图的目的是用来后续计算出度来找到终端节点
  • 同时构建原图的出度数组,后续就会用该数组来找到安全节点
  • 然后将得到的所有终端节点都入队列,后续操作该队列即可得到所有终端节点
  • 然后将得到的安全节点(所有终端节点都是安全节点)保存到集合中
  • 然后通过逆向拓扑排序计算得到安全节点:
    • 首先从队列中取出一个安全节点
    • 然后查看它的邻接节点
      • 然后将邻接节点的出度减1
      • 如果此时出度为0,那么说明其为安全节点,将其入队列和集合中
  • 具体细节可以参考下面的代码
  • 最后返回结果即可

八【时空频度】

  • 时间复杂度: O ( m + n ) O(m + n) O(m+n), m m m为图的节点数, n n n为图的边数
  • 空间复杂度: O ( m + n ) O(m + n) O(m+n), m m m为图的节点数, n n n为图的边数

九【代码实现】

  1. Java语言版
java 复制代码
class Solution {
    public List<Integer> eventualSafeNodes(int[][] graph) {
        // 图中节点的个数
        int n = graph.length;
        // 用来存储反向图
        List<List<Integer>> reverseGraph = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            reverseGraph.add(new ArrayList<>());
        }
        // 每个节点的出度
        int[] outDegree = new int[n];
        // 构建反向图和出度数组
        for (int i = 0; i < n; i++) {
            outDegree[i] = graph[i].length;
            for (int neighbor : graph[i]) {
                reverseGraph.get(neighbor).add(i);
            }
        }
        // 初始化队列,将所有终端节点(出度为0的节点)加入队列
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (outDegree[i] == 0) {
                queue.offer(i);
            }
        }
        // 用集合存储安全节点
        Set<Integer> safeNodes = new HashSet<>(queue);
        // 逆向拓扑排序
        while (!queue.isEmpty()) {
            int node = queue.poll();
            for (int neighbor : reverseGraph.get(node)) {
                outDegree[neighbor]--;
                if (outDegree[neighbor] == 0) {
                    safeNodes.add(neighbor);
                    queue.offer(neighbor);
                }
            }
        }
        // 返回安全节点的升序列表
        List<Integer> res = new ArrayList<>(safeNodes);
        Collections.sort(res);
        return res;
    }
}
  1. Python语言版
python 复制代码
class Solution:
    def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
        # 图中节点的个数
        n = len(graph)
        # 用来存储反向图
        reverse_graph = defaultdict(list)
        # 每个节点的出度
        out_degree = [0] * n
        # 构建反向图和出度数组
        for i, neighboors in enumerate(graph):
            out_degree[i] = len(neighboors)
            for neighboor in neighboors:
                reverse_graph[neighboor].append(i)
        # 初始化队列,将所有终端节点(出度为0的节点)加入队列
        queue = deque(i for i in range(n) if out_degree[i] == 0)
        # 用集合存储安全节点
        safe_nodes = set(queue)
        # 逆向拓扑排序
        while queue:
            node = queue.popleft()
            for neighboor in reverse_graph[node]:
                out_degree[neighboor] -= 1
                if out_degree[neighboor] == 0:
                    safe_nodes.add(neighboor)
                    queue.append(neighboor)
        # 返回安全节点的升序列表
        return sorted(safe_nodes)
  1. C++语言版
cpp 复制代码
class Solution {
public:
    vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
        // 图中节点的个数
        int n = graph.size();
        // 用来存储反向图
        vector<vector<int>> reverseGraph(n);
        // 每个节点的出度
        vector<int> outDegree(n, 0);
        // 构建反向图和出度数组
        for (int i = 0; i < n; i++) {
            outDegree[i] = graph[i].size();
            for (int neighbor : graph[i]) {
                reverseGraph[neighbor].push_back(i);
            }
        }
        // 初始化队列,将所有终端节点(出度为0的节点)加入队列
        queue<int> q;
        // 用集合存储安全节点
        unordered_set<int> safeNodes;
        for (int i = 0; i < n; i++) {
            if (outDegree[i] == 0) {
                q.push(i);
                safeNodes.insert(i);
            }
        }
        // 逆向拓扑排序
        while (!q.empty()) {
            int node = q.front();
            q.pop();
            for (int neighbor : reverseGraph[node]) {
                outDegree[neighbor]--;
                if (outDegree[neighbor] == 0) {
                    safeNodes.insert(neighbor);
                    q.push(neighbor);
                }
            }
        }
        // 返回安全节点的升序列表
        vector<int> res(safeNodes.begin(), safeNodes.end());
        sort(res.begin(), res.end());
        return res;
    }
};

十【提交结果】

  1. Java语言版

  2. Python语言版

  3. C++语言版

相关推荐
Jerry7 分钟前
LeetCode 459. 重复的子字符串
算法
海石3 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石3 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry5 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding5 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL7 小时前
我的数据结构3——链表(link list)
数据结构·链表
a1117767 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜7 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry8 小时前
LeetCode 541. 反转字符串 II
算法
Jerry8 小时前
LeetCode 344. 反转字符串
算法