力扣207.课程表

news2024/10/7 20:25:35

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。

  • 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

提示:

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= 5000
  • prerequisites[i].length == 2
  • 0 <= ai, bi < numCourses
  • prerequisites[i] 中的所有课程对 互不相同

这道题说人话就算判断这个有向图有没有环路。

我们知道拓扑排序可以用来判断是否有环路,不过仅仅是判断环路也可以直接用dfs,不需要完全写出拓扑排序,毕竟拓扑排序相比dfs还是更复杂一些。

下面给出上述两种思路的代码

1.拓扑排序判断

我的拓扑排序的代码思路是参考王道书介绍的思路

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph(numCourses, vector<int>(numCourses));//邻接矩阵
        vector<int> indegree(numCourses, 0);//每个节点的入度
        for (int i = 0; i < prerequisites.size(); i++) {
            graph[prerequisites[i][1]][prerequisites[i][0]] = 1;//构造邻接矩阵
            indegree[prerequisites[i][0]]++;//修改该点的入度
        }
        stack<int> stk;//用栈或队列都可以
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0)//入度为0的点入栈
                stk.push(i);
        }
        int cnt = 0;//记录节点的数量
        while (!stk.empty()) {
            int i = stk.top();
            stk.pop();
            //如果是正常的拓扑排序,这里就可以输出节点了,本题只要求判断环路
            cnt++;//出栈的节点入度都为0,cnt++
            for (int j = 0; j < numCourses; j++) {//遍历当前节点的临边
                if (graph[i][j] != 0) {
                    graph[i][j] = 0;//删除这条有向边
                    indegree[j]--;//相邻边的入度减1
                    if (indegree[j] == 0)//如果临边入度为0,入栈
                        stk.push(j);
                }
            }

        }
        return cnt == numCourses;//最终的判断条件,cnt不为n,则有环路
        //因为有环路的情况下环路上的节点的入度不可能为0,就不会入栈,所以最后cnt的值不为n
    }
};

下面用一张图作为示例:

一开始只有0的度为0,所以0入栈,出栈时会执行这一段代码

            for (int j = 0; j < numCourses; j++) {//遍历当前节点的临边
                if (graph[i][j] != 0) {
                    graph[i][j] = 0;//删除这条有向边
                    indegree[j]--;//相邻边的入度减1
                    if (indegree[j] == 0)//如果临边入度为0,入栈
                        stk.push(j);
                }
            }

那么就把从0开始的弧都删掉,并把弧所指向的节点的入度减1

这时候1和2的入度就为0了,入栈。然后2先出栈,把2->3这段弧也删掉

但是此时3的入度不为0,也就先不会入栈。然后1出栈,把1->3这段弧删掉

这时候3的入度也为0,3入栈。3再出栈,3出栈反正啥也不会做。

最终,4个节点全部入了栈,因此cnt==n成立,该图是无环有向图。

如果是有环图,可以自己试一下,最后图里会剩下环,因为环上所有节点的入度都不为0,因此不会入栈。

2.dfs


class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        graph=vector<vector<int>>(numCourses, vector<int>(numCourses));//邻接矩阵
        vis = vector<int>(numCourses, 0);//visit数组
        valid = true;//标记是否存在环路
        vector<int> indegree(numCourses, 0);
        for (int i = 0; i < prerequisites.size(); i++) {
            graph[prerequisites[i][1]][prerequisites[i][0]] = 1;
        }
        //以上和拓扑排序都是一样的步骤
        for (int i = 0; i < numCourses; i++) {
            dfs(i);
        }
        
        return valid;
    }
private:
    void dfs(int u) {
        if (!valid) return;//如果存在环路直接返回
        if (vis[u] == 1) { valid = false; return; }//如果当前节点正在遍历,则表明存在环路
        if (vis[u] == 2) return;//如果当前节点已经遍历完成,直接返回
        vis[u] = 1;//vis置为1,表示当前节点正在dfs中
        for (int v = 0; v < vis.size(); v++) {//遍历当前节点的临边
            if (graph[u][v] != 0) {
                dfs(v);
                if (!valid) return;//有环直接退出
            }
        }
        vis[u] = 2;//置为2,表示当前节点已经走过了
    }
    bool valid;
    vector<int> vis;
    vector<vector<int>> graph;
};

关键点在于vis数组的值有1和2的区分

画图解释,设1代表蓝色,2代表红色

从0出发,进行一次dfs,假设路径是0-1-3-4,那么最后1,3,4会变成红色,0还是蓝色

之后dfs会走2这条路

走到2后,会再次调用dfs(3),但是因为

if (vis[u] == 2) return;//如果当前节点已经遍历完成,直接返回

所以valid还是true;

如果是有环路的情况,从0出发进行一次dfs

走到2后会再次调用dfs(0),而

if (vis[u] == 1) { valid = false; return; }//如果当前节点正在遍历,则表明存在环路

所以有环路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1589555.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024-3-29 群讨论:如何看到一个线程的所有 JFR 事件

以下来自本人拉的一个关于 Java 技术的讨论群。关注公众号&#xff1a;hashcon&#xff0c;私信拉你 如何查看一个线程所有相关的 JFR 事件 一般接口响应慢&#xff0c;通过日志可以知道是哪个线程&#xff0c;但是如何查看这个线程的所有相关的 JFR 事件呢&#xff1f;JMC 有…

77、WAF攻防——权限控制代码免杀异或运算变量覆盖混淆加密传参

文章目录 WAF规则webshell免杀变异 WAF规则 函数匹配 工具指纹 webshell免杀变异 php 传参带入 eval可以用assert来替换,assert也可以将字符串当作php代码执行漏洞 php 变量覆盖 php 加密 使用加密算法对php后门进行加密 php 异或运算 简化:无字符webshellP 无数字字母rc…

WPS二次开发系列:Gradle版本、AGP插件与Java版本的对应关系

背景 最近有体验SDK的同学反馈接入SDK出现报错&#xff0c;最终定位到原因为接入的宿主app项目的gradle版本过低导致&#xff0c;SDK兼容支持了android11的特性&#xff0c;需要对应的gradle插件为支持android11的版本。 现象 解决方案 将gradle版本升级至支持android11的插件版…

【数据结构与算法】之8道顺序表与链表典型编程题心决!

个人主页&#xff1a;秋风起&#xff0c;再归来~ 数据结构与算法 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 目录 1、顺序表 1.1 合并两个有序数组 1.2 原地移除数组中所有的元素va…

Python+selenium搭建Web自动化测试框架

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在程序员的世界中&#xff0c;一切重复性的工作&#xff0c;都应该通过程序自动执行。「自动化测…

Uniapp+基于百度智能云完成AI视觉功能(附前端思路)

本博客使用uniapp百度智能云图像大模型中的AI视觉API&#xff08;本文以物体检测为例&#xff09;完成了一个简单的图像识别页面&#xff0c;调用百度智能云API可以实现快速训练模型并且部署的效果。 uniapp百度智能云AI视觉页面实现 先上效果图实现过程百度智能云Easy DL训练图…

Proxmox VE qm 方式一键创建Windows虚拟机

前言 实现qm 方式一键创建Windows虚拟机&#xff0c;提高效率。 qm 一键创建Windows虚拟机 以下实现在线下载镜像&#xff0c;创建虚拟机&#xff0c;安装系统需要自己手动安装哦&#xff0c;如果想实现全自动安装系统&#xff0c;建议部署自己的内网pxe server 系统参考各参…

蓝桥杯——松散子序列

题目 分析 很明显的动态规划问题&#xff0c;每次我们都取当前位置的最大值就可&#xff0c;从头开始&#xff0c;dp[i]max(dp[i-2],dp[i-3])num[i-3]. 代码 ninput() num[] for i in n:num.append(ord(i)-96) dp[0]*(len(num)3) for i in range(3,len(num)3):dp[i]max(dp[i…

基于特征的多模态生物信号信息检索与自相似矩阵:专注于自动分割

论文地址&#xff1a;Biosensors | Free Full-Text | Feature-Based Information Retrieval of Multimodal Biosignals with a Self-Similarity Matrix: Focus on Automatic Segmentation (mdpi.com) 论文源码&#xff1a;无 期刊&#xff1a;biosensors 这篇论文提出了一种基…

java数据结构与算法刷题-----LeetCode172. 阶乘后的零

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 数学&#xff1a;阶乘的10因子个数数学优化:思路转变为求5的倍数…

JQuery ajax请求

实现卡号绑定功能 通过 jQuery ajax 请求用户列表数据。 API 接口地址 卡号列表 js/cardnolist.json 接口响应示例 补全 index.js 中空缺代码&#xff0c;实现用户输入完卡号与密码后&#xff0c;与 ajax 请求到的卡号数据进行比对&#xff0c;当卡号和密码匹配成功时&a…

使用 Docker 部署 Linux-Command 命令搜索工具

1&#xff09;介绍 Linux-Command GitHub&#xff1a;https://github.com/jaywcjlove/linux-command Linux-Command 仓库搜集了 580 多个 Linux 命令&#xff0c;是一个非盈利性的仓库&#xff0c;生成了一个 Web 网站方便使用&#xff0c;目前网站没有任何广告&#xff0c;内…

JVM参数列表

-client :设置JVM使用client模式,特点启动较快(神机不明显(I5/8G/SSD)) -server :设置JVM使用server模式。64位JDK默认启动该模式 -agentlib:libname[options] :用于加载本地的lib -agentlib:hprof :用于获取JVM的运行情况 -agentpath:pathnamep[options] :加载制定路径的本…

共享内存详解

共享内存是操作系统在内存中开辟一块空间&#xff0c;通过页表与共享区建立映射关系&#xff0c;使两个进程看到同一份资源&#xff0c;实现进程间通信。 1、创建共享内存 参数&#xff1a;第一个参数为key&#xff0c;一般使用ftok()函数生成&#xff0c;key值不能冲突&#…

副业天花板流量卡推广,小白也可轻松操作

在如今的互联网时代&#xff0c;手机已经不仅仅是一款工具&#xff0c;更像是我们生活中的一部分&#xff0c;那么手机卡也是必需品&#xff0c;但存在的问题就是:很多手机卡的月租很贵&#xff0c;流量也不够用。所以大家都在寻找一个月租低&#xff0c;流量多的卡&#xff0c…

Java-Doc

Java-Doc javdoc命令是用来生成自己的API文档的 参数信息&#xff1a;author作者名version版本号since知名需要最早使用的jdk版本param参数名return返回值情况throws异常抛出情况 1.参数信息的使用&#xff1a; 未完待续... ...

【好消息】思维100活动历年真题模拟题700多道上线了,供反复吃透

今天是星期五&#xff0c;距离4月20日举办的上海小学生 2024年春季思维100活动线上比赛还有8天的时间&#xff0c;明天、后天的周末是可以用来备考的大块时间&#xff0c;报名了的同学要充分利用了。 为了帮助各位小朋友了解思维100活动的历年考试真题、官方发布的参考样题&…

【数组】5螺旋矩阵

这里写自定义目录标题 一、题目二、解题精髓-循环不变量三、代码 一、题目 给定⼀个正整数 n&#xff0c;⽣成⼀个包含 1 到 n^2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的正⽅形矩阵。 示例: 输⼊: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ] 二、解题精髓…

docker最简单教程(使用dockerfile构建环境)

一 手里有的东西 安装好的docker+dockerfile 二 操作 只需要在你的dockerfile文件下执行命令 docker build -t="xianhu/centos:gitdir" . 将用户名、操作系统和tag进行修改就可以了,这就相当于在你本地安装了一个docker环境,然后执行 docker run -it xianhu/ce…

阿里云4核16G服务器可以用来做什么?

阿里云4核16G服务器可以用来做什么&#xff1f;可用来搭建游戏服务器&#xff0c;阿里云4核16G服务器10M带宽30元1个月、90元3个月&#xff0c;优惠活动 aliyunfuwuqi.com/go/youhui 阿里云4核16G服务器可以用来做什么&#xff1f;除了搭建游戏服务器&#xff0c;还可以用来哪…