LeetCode 热题 100 | 图论(二)

目录

[1 基础知识](#1 基础知识)

[1.1 什么是拓扑排序](#1.1 什么是拓扑排序)

[1.2 如何进行拓扑排序](#1.2 如何进行拓扑排序)

[1.3 拓扑排序举例](#1.3 拓扑排序举例)

[2 207. 课程表](#2 207. 课程表)

[3 210. 课程表 II](#3 210. 课程表 II)


菜鸟做题,语言是 C++

1 基础知识

1.1 什么是拓扑排序

含义:根据节点之间的依赖关系来生成一个有序的序列。

应用:

  • 在项目管理中,按照任务之间的的依赖关系来安排执行顺序。
  • 在编译原理中,按照编译单元的依赖关系来确定编译单元的生成顺序。
  • 在程序设计中,按照模块之间的依赖关系来确定模块的加载和执行顺序。

突然想起来好像在操作系统原理里学过!

1.2 如何进行拓扑排序

排序步骤:

  1. 选择入度为 0 的节点加入到排序结果中,并从图中移除该节点以及它的所有出边;
  2. 重复步骤 1,每次都选择入度为 0 的节点加入到排序结果中,并更新图中剩余节点的入度;
  3. 重复步骤 1 和 2,直到所有的节点都被加入到排序结果中,或者不再存在入度为 0 的节点。

名词解释:

  • → 节点 属于节点的 入边
  • 节点 → 属于节点的 出边

如果图中不再存在入度为 0 的节点,且并非所有的节点都被加入到排序结果中,那么说明原图中存在环。综上,拓扑排序的主要功能是帮助安排顺序,顺带帮助检测图中有没有环。

1.3 拓扑排序举例

下图描述了一个拓扑排序过程。

① 入度为 0 的节点有 B、D,我们任意选择 B,并删除它的出边:

② 入度为 0 的节点有 A、D,我们任意选择 A,并删除它的出边;更新后,入度为 0 的节点有 C、D,我们任意选择 C,并删除它的出边:

③ 入度为 0 的节点有 D,我们选择 D,并删除它的出边;更新后,入度为 0 的节点有 F,我们选择 F,并删除它的出边:

以此类推,不再赘述。通过 "任意选择" 一词可以看出,拓扑排序的结果不止一种。

2 207. 课程表

感觉解题方法和拓扑排序关系不大,更多的是从题意出发;想了很久要怎么才能把思路表述清楚,最终认为还是看代码来得快 TT

解题思路:假设有先修课程对 [0,1] 和 [0,2]

  • 初始化:获取每门课程的先修课程数组,比如:0 对应 [1,2]
  • 初始化:设置记录访问状态的数组,并将访问状态全部置为 0
  • 循环:访问每门课程,判断它的先修课程是否已经被全部修完
  • 如果它的先修课程未被全部修完,则表明无法完成所有课程的学习

访问状态:

  • visited[course] = 0:该门课程还未被学习
  • visited[course] = 1:该门课程正在被学习
  • visited[course] = 2:该门课程已经被学习

针对 "该门课程正在被学习" 的说法,需要说明的是,这是算法题而不是现实生活!如果一门课正在被学习,不是代表学习它需要一段时间,而是代表它的先修课程不可能被修完,导致我们永远学不了它。

cpp 复制代码
class Solution {
public:
    vector<int> visited;
    vector<vector<int>> mustFinish;
    bool possible = true;

    void helper(int course) {
        // 指明该门课程正在被学习
        visited[course] = 1;
        // 判断其先修课程是否已被修完
        for (auto & preCourse : mustFinish[course]) {
            // 递归访问未被学习的先修课程
            if (visited[preCourse] == 0) {
                helper(preCourse);
                if (!possible) return;
                // 先修课程正在被学习(因为卡住了)
            } else if (visited[preCourse] == 1) {
                possible = false;
                return;
            }
        }
        // 指明该门课程已经被学习
        visited[course] = 2;
    }

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        visited.resize(numCourses);
        mustFinish.resize(numCourses);

        // 获取每门课程的先修课程数组
        for (auto & p : prerequisites) {
            mustFinish[p[0]].push_back(p[1]);
        }
        // 循环访问每门课程
        for (int i = 0; i < numCourses && possible; ++i) {
            helper(i);
        }

        return possible;
    }
};

3 210. 课程表 II

207. 课程表 的基础上,增加一个数据结构来存储课程顺序即可。

唯一区别在于:

cpp 复制代码
for (int i = 0; i < numCourses && possible && !visited[i]; ++i)

新增条件 !visited[i],不允许已经被访问过的课程再被访问,避免了课程重复被学习。

cpp 复制代码
class Solution {
public:
    vector<int> visited;
    vector<vector<int>> mustFinish;
    bool possible = true;
    vector<int> ans;

    void helper(int course) {
        visited[course] = 1;
        for (auto & preCourse : mustFinish[course]) {
            if (visited[preCourse] == 0) {
                helper(preCourse);
                if (!possible) return;
            } else if (visited[preCourse] == 1) {
                possible = false;
                return;
            }
        }
        visited[course] = 2;
        ans.push_back(course);
    }

    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        visited.resize(numCourses);
        mustFinish.resize(numCourses);

        for (auto & p : prerequisites) {
            mustFinish[p[0]].push_back(p[1]);
        }

        for (int i = 0; i < numCourses && possible && !visited[i]; ++i) {
            helper(i);
        }

        if (possible) return ans;
        return {};
    }
};
相关推荐
寂静山林3 分钟前
UVa 11853 Paintball
算法
Theodore_102239 分钟前
深度学习(10)模型评估、训练与选择
人工智能·深度学习·算法·机器学习·计算机视觉
五条凪44 分钟前
Verilog-Eval-v1基准测试集搭建指南
开发语言·人工智能·算法·语言模型
是店小二呀1 小时前
从“算法思维”到“算子思维”:我在昇腾AI开发中的认知跃迁
人工智能·算法
仰泳的熊猫1 小时前
LeetCode:72. 超级次方
数据结构·c++·算法·leetcode
闻缺陷则喜何志丹1 小时前
【超音速专利 CN118134841A】一种光伏产品缺陷检测AI深度学习算法
人工智能·深度学习·算法·专利·光伏·超音速
爱看科技2 小时前
微美全息(NASDAQ:WIMI)容错量子计算赋能,大规模机器学习模型高效量子算法获突破
算法·机器学习·量子计算
_dindong2 小时前
牛客101:递归/回溯
数据结构·c++·笔记·学习·算法·leetcode·深度优先
刃神太酷啦3 小时前
力扣校招算法通关:双指针技巧全场景拆解 —— 从数组操作到环检测的高效解题范式
java·c语言·数据结构·c++·算法·leetcode·职场和发展
西瓜树枝3 小时前
遗传算法与属性约简:原理、代码与参数配置
算法