想要精通算法和SQL的成长之路 - 课程表II

这篇具有很好参考价值的文章主要介绍了想要精通算法和SQL的成长之路 - 课程表II。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

想要精通算法和SQL的成长之路 - 系列导航

一. 课程表II (拓扑排序)

原题链接
想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库

1.1 拓扑排序

核心知识:

  1. 拓扑排序是专门应用于有向图的算法。
  2. BFS的写法就叫拓扑排序,核心就是:让入度为0的节点入队。
  3. 拓扑排序的结果不唯一。
  4. 同时拓扑排序有一个重要的功能:能够检测有向图中是否存在环。

我们先分析一下本题:

  1. 先说下课程,课程有它自己的一个先后的依赖顺序。符合 “有向”
  2. 想要学习某个课程,它的前缀课程可能有多个。那么我们可以用 “度” 的概念去标识衡量。这里是入度。

先用图来解释下本次算法的核心方向(摘自leetcode题解):

  1. 想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库
  2. 想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库
  3. 想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库
  4. 想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库
  5. 想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库
  6. 想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库

说白了就是:

  1. 不断地找入度为0的节点,然后剔除。剔除的同时维护后续节点的入度数
  2. 以此类推。

1.2 题解

那么本题我们该如何解?我们一步步来,我们以numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]] 为例来说。官方解释:

  • 总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
public int[] findOrder(int numCourses, int[][] prerequisites) {

}

那么我们可以知道这个二维数组prerequisites中,第二个数字代表:”前缀“,第一个数字代表:”后缀“。[1,0] 用图表示就是:0->1同时我们还可以计算出,此时1这个节点的入度应该+1。因为0指向了1。

1.首先,我们要对入参做一个校验:

// 1. 先判断数组或者numCourses为空的情况
if (numCourses <= 0) {
    return new int[0];
}

2.我们需要遍历二维数组prerequisites中,拿到所有节点的入度以及这个拓扑图的结构。

  • 我们用inDegree[]数组表示各个节点的入度。
  • 用一个HashSet数组表示邻接表的结果。数组的索引代表的是节点的值。数组值(即HashMap)代表这个节点的所有后继节点。以0为例,它的后继节点有1和2。
// 2. 用inDegree[] 数组表示每个节点的入度数。
// 同时维护拓扑图的结构例如:0->1,0->2
HashSet<Integer>[] adj = new HashSet[numCourses];
// 初始化下
for (int i = 0; i < numCourses; i++) {
    adj[i] = new HashSet<>();
}
// 构建入度
int[] inDegree = new int[numCourses];
for (int[] p : prerequisites) {
    // 入度+1
    inDegree[p[0]]++;
    // 把当前节点的后继节点存储起来
    adj[p[1]].add(p[0]);
}

3.我们用一个队列,用来存储入度为0的节点。

// 3.准备个队列,存储入度为0的节点
LinkedList<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
    if (inDegree[i] == 0) {
        queue.offer(i);
    }
}

为啥要这一个步骤?如果发现没有入度为0的节点,说明啥,成环了,那本题就无解。如图:
想要精通算法和SQL的成长之路 - 课程表II,算法,sql,数据库
4.如果存在入度为0的节点,我们开始往后递归,做2.1节的内容。

  • 先拿到入度为0的节点,删除它(把他放入结果集)。
  • 同时维护后继节点的入度。
  • 如果后继节点入度数-1后,为0。那么同样放入到队列中递归。
// 结果集
int[] res = new int[numCourses];
// 统计结果集中的个数
int count = 0;
while (!queue.isEmpty()) {
    // 入度为0的节点,我们弹出
    Integer head = queue.poll();
    // 放入结果集
    res[count++] = head;
    // 当前入度为0节点对应的后继节点。如果是0 --> 1,2
    HashSet<Integer> nextNodeList = adj[head];
    // 更新后继节点的入度
    for (Integer nextNode : nextNodeList) {
        // 对应的后继节点的入度要减1,
        inDegree[nextNode]--;
        // 如果入度-1后,为0了。再入队
        if (inDegree[nextNode] == 0) {
            queue.offer(nextNode);
        }
    }
}

最后,我们只需要关心结果集个数是否和题干中的numCourses一致。一致的话返回我们构建的结果集,否则本题为空解:

// 如果遍历完了,发现count数量和 numCourses一致,说明有一个正确的结果集
if (count == numCourses) {
    return res;
}
return new int[0];

最终完整代码如下:

public class Test210 {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        // 1. 先判断数组或者numCourses为空的情况
        if (numCourses <= 0) {
            return new int[0];
        }
        // 2. 用inDegree[] 数组表示每个节点的入度数。
        // 同时维护拓扑图的结构例如:0->1,0->2
        HashSet<Integer>[] adj = new HashSet[numCourses];
        // 初始化下
        for (int i = 0; i < numCourses; i++) {
            adj[i] = new HashSet<>();
        }
        // 构建入度
        int[] inDegree = new int[numCourses];
        for (int[] p : prerequisites) {
            // 入度+1
            inDegree[p[0]]++;
            // 把当前节点的后继节点存储起来
            adj[p[1]].add(p[0]);
        }
        // 3.准备个队列,存储入度为0的节点
        LinkedList<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }
        // 结果集
        int[] res = new int[numCourses];
        // 统计结果集中的个数
        int count = 0;
        while (!queue.isEmpty()) {
            // 入度为0的节点,我们弹出
            Integer head = queue.poll();
            // 放入结果集
            res[count++] = head;
            // 当前入度为0节点对应的后继节点。如果是0 -- > 1,2
            HashSet<Integer> nextNodeList = adj[head];
            // 更新后继节点的入度
            for (Integer nextNode : nextNodeList) {
                // 对应的next节点的入度要减1,
                inDegree[nextNode]--;
                // 如果入度-1后,为0了。再入队
                if (inDegree[nextNode] == 0) {
                    queue.offer(nextNode);
                }
            }
        }
        // 如果遍历完了,发现count数量和 numCourses一致,说明有一个正确的结果集
        if (count == numCourses) {
            return res;
        }
        return new int[0];
    }
}

这个题目,算是课程表系列的第二道了。第一道:207. 课程表,做法和上面一模一样,只不过返回数组的地方返回true/false即可。文章来源地址https://www.toymoban.com/news/detail-705782.html

到了这里,关于想要精通算法和SQL的成长之路 - 课程表II的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 想要精通算法和SQL的成长之路 - 滑动窗口和大小根堆

    想要精通算法和SQL的成长之路 - 系列导航 先来说下大小根堆是什么: 大根堆:栈顶元素最大(上图左侧部分),栈底至栈顶元素值递增。 小根堆:栈顶元素最小(上图右侧部分),栈底至栈顶元素值递减。 在 Java 当中,可以用什么来表示大小根堆? 小根堆: 大根堆: 大小

    2024年02月07日
    浏览(43)
  • 想要精通算法和SQL的成长之路 - 二叉树的序列化和反序列化问题

    想要精通算法和SQL的成长之路 - 系列导航 二叉树的层序遍历 像这种从上至下并且按层打印的,可以称之为 二叉树的广度优先搜索( BFS ) 。而这类算法往往借助 队列的一个先入先出特性 来实现。 那么有这么几个步骤: 1.特殊处理还有初始化动作。 2. BFS 循环: 最终完整代

    2024年02月07日
    浏览(49)
  • LeetCode:207. 课程表、210. 课程表 II(拓扑排序 C++)

    目录 207. 课程表 题目描述: 实现代码与解析: 拓扑排序 210. 课程表 II 题目描述: 实现代码与解析: 拓扑排序 原理思路:         你这个学期必须选修  numCourses  门课程,记为  0  到  numCourses - 1  。 在选修某些课程之前需要一些先修课程。 先修课程按数组  prereq

    2024年02月09日
    浏览(42)
  • 自制课程表小程序

    微信开发者工具:稳定版 Stable Build 根据自己电脑的系统下载对应的版本 完整代码连接: https://pan.baidu.com/s/1VbgPOS6CUOae8vg2axhGIg?pwd=hk9e 提取码:hk9e 先把完整代码的压缩包解压,并记住这个路径,后面要用 1.打开下载好的微信开发工具, 注意不要用游客登录 ,我一开始就是游客

    2024年02月09日
    浏览(90)
  • leetcode207. 课程表

    你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先

    2023年04月24日
    浏览(43)
  • 210. 课程表 II Python

    现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1 。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。 返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完

    2024年02月14日
    浏览(40)
  • java实现课程表 II

    题目: 现在你总共有  numCourses  门课需要选,记为  0  到  numCourses - 1 。给你一个数组  prerequisites  ,其中  prerequisites[i] = [ai, bi]  ,表示在选修课程  ai  前  必须  先选修  bi  。 例如,想要学习课程  0  ,你需要先完成课程  1  ,我们用一个匹配来表示: [0,1]  。

    2024年02月09日
    浏览(44)
  • 微信小程序实现课程表

    2.1 获取当前日期一周数据 Date.getDay(): getDay() 方法返回指定日期是星期几(从 0 到 6,星期日为 0,星期一为 1,依此类推。)。 Date.getDate(): getDate() 方法返回指定日期在月中的第几天(从 1 到 31)。 Date.setDate(day): setDate() 方法将月份中的某一天设置为日期对象。 Date.getFullYear(

    2024年02月09日
    浏览(50)
  • leetcode 630. 课程表 III

    这里有 n 门不同的在线课程,按从 1 到 n 编号。给你一个数组 courses ,其中 courses[i] = [durationi, lastDayi] 表示第 i 门课将会 持续 上 durationi 天课,并且必须在不晚于 lastDayi 的时候完成。 你的学期从第 1 天开始。且不能同时修读两门及两门以上的课程。 返回你最多可以修读的课

    2024年02月16日
    浏览(43)
  • 【图论】Leetcode 207. 课程表【中等】

    你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先

    2024年04月14日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包