【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 <= graph[i].length <= n 0<=graph[i].length<=n
  • 0 < = g r a p h [ i ] [ j ] < = n − 1 0 <= graph[i][j] <= n - 1 0<=graph[i][j]<=n−1
  • g r a p h [ i ] graph[i] graph[i] 按严格递增顺序排列。
  • 图中可能包含自环。
  • 图中边的数目在范围 [ 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++语言版

相关推荐
Dream_Snowar11 分钟前
速通Python 第四节——函数
开发语言·python·算法
1nullptr23 分钟前
三次翻转实现数组元素的旋转
数据结构
Altair澳汰尔24 分钟前
数据分析和AI丨知识图谱,AI革命中数据集成和模型构建的关键推动者
人工智能·算法·机器学习·数据分析·知识图谱
TT哇28 分钟前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
A懿轩A1 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
Python机器学习AI1 小时前
分类模型的预测概率解读:3D概率分布可视化的直观呈现
算法·机器学习·分类
吕小明么2 小时前
OpenAI o3 “震撼” 发布后回归技术本身的审视与进一步思考
人工智能·深度学习·算法·aigc·agi
1 9 J2 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
程序员shen1616112 小时前
抖音短视频saas矩阵源码系统开发所需掌握的技术
java·前端·数据库·python·算法
汝即来归2 小时前
选择排序和冒泡排序;MySQL架构
数据结构·算法·排序算法