【LeetCode题目拓展】第207题 课程表 拓展(拓扑排序、Tarjan算法、Kosaraju算法)

文章目录

    • 一、拓扑排序题目
    • 二、题目拓展
      • 1. 思路分析
      • 2. tarjan算法
      • 3. kosaraju算法

一、拓扑排序题目

最近在看一个算法课程的时候看到了一个比较好玩的题目的扩展,它的原题如下:
在这里插入图片描述
对应的LeetCode题目为 207. 课程表

这个题目本身来说比较简单,就是一道标准的拓扑排序题目,解法代码如下:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;public class Solution {public boolean scheduleCourses(int[][] prerequisites) {// 记录每个节点的入度int[] degree = new int[prerequisites.length]; // 记录每个节点是哪些节点的前置节点List<Integer>[] neighbors = new List[prerequisites.length]; // 记录当前可以选择的节点Queue<Integer> available = new LinkedList<>();for (int i = 0; i < prerequisites.length; i++) {degree[i] = prerequisites[i].length;neighbors[i] = new ArrayList<>();if (degree[i] == 0) {available.offer(i);}}for (int i = 0; i < prerequisites.length; i++) {for (int to : prerequisites[i]) {neighbors[to].add(i);}}int count = 0;while (!available.isEmpty()) {// 1. 取出available中一个节点// 2. 遍历该节点的邻居节点//   a. 将该邻居节点的入度减一//   b. 若此时邻居节点的入度为0,加入available中// 3. 处理节点数count加一int cur = available.poll();for (int to: neighbors[cur]) {degree[to]--;if (degree[to] == 0) {available.offer(to);}}count++;}return count == prerequisites.length;}
}

二、题目拓展

这道题目整体难度不大,但是课程里提出了对于该题目的拓展非常有意思。
题目拓展:假如学生有同时上多门课的能力,那么是否可以根据他最多能同时上课的数量,来判断对于指定的课程安排他是否可以完成。

1. 思路分析

初看这个拓展时,我的想法是在有向图里找环的方式来实现,比如找到整个有向图中包含节点数目最多的环,判断这个数目是否超过了该同学最多能同时上课的数量。但这种方式有一个比较大的问题,就是如何定义什么是环,以及该定义下的环是否满足需要。根据这两个问题,我举出了如下两个图:
在这里插入图片描述
在这里插入图片描述
可以看到,对于第一个图来说,它可以说包含3个环,这种情况下该以哪个环的节点数来度量呢?对于第二个图,两个环共用了一个节点,此时只计算一个环的节点数并不能满足题目的需求。

此时仔细观察图二中的这种场景,我发现只有这两个环都计算节点数然后与可以最大同时上课数比较才成立。结合图一,可以得出一个结论,即当图中一个节点与另一个节点可以互相到达时,它们需要被计算在一起。这不正好就是有向图的强连通分量的定义吗?

于是,这个问题就转换成了,求一个有向图中包含节点数最大的连通分量的节点数。整个思路豁然开朗了。(由此可以看出,拿到一些特化的问题时把问题迁移到一种常识性问题是非常重要的!)

根据这种思路,我们需要求有向图中规模最大的连通分量的节点数,并且把它和学生最大同时上课数进行比较,就可以得到答案了。

详细图示如下:
在这里插入图片描述
将每一个强连通分量视为学生需要同时上的课程,即可以得到一个强连通分量缩点的拓扑排序,之后学生可以按照正常的拓扑排序顺序对缩点进行上课即可,如下所示:
在这里插入图片描述

求解有向图中的强连通分量问题一般有两种算法,tarjan算法和kosaraju算法,此处不赘述两种算法的细节,感兴趣的可以自行搜索,此处只把各自解法列在下方。

2. tarjan算法

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;public class TarjanSolution {// 图的邻接表表示形式,记录每个节点是哪些节点的前置节点private List<Integer>[] neighbors;private int skill;private int[] dfn;private int[] low;private int idx;private Stack<Integer> stack;private boolean[] isInStack; // 用于快速判断是否在栈中public boolean scheduleCourses(int[][] prerequisites, int skill) {if (skill < 1) {return false;}this.skill = skill;// 初始化相关存储结构initGraph(prerequisites);// 最大连通分量的节点数return tarjan();}private void initGraph(int[][] prerequisites) {neighbors = new List[prerequisites.length]; for (int i = 0; i < prerequisites.length; i++) {neighbors[i] = new LinkedList<>();}for (int i = 0; i < prerequisites.length; i++) {for (int to : prerequisites[i]) {neighbors[to].add(i);}}}private boolean tarjan() {this.dfn = new int[neighbors.length];this.low = new int[neighbors.length];this.idx = 0;this.isInStack = new boolean[neighbors.length];this.stack = new Stack<Integer>();for (int i = 0; i < neighbors.length; ++i) {if (dfn[i] == 0) {if (!tarjanRecursion(i)) { // 如果已经失败,则提前结束return false;}}}return true;}private boolean tarjanRecursion(int cur) {// 入栈stack.push(cur);isInStack[cur] = true;//初始化当前节点的时间戳dfn[cur] = low[cur] = ++idx;// 遍历当前节点的邻居节点,共3类:1. 没被找过的;2. 在栈里的;3. 已经构成联通分量的(这种直接跳过即可)for (int neighbor: neighbors[cur]) {// 如果没被找过if (dfn[neighbor] == 0) {if (!tarjanRecursion(neighbor)) { // 如果已经失败,则提前结束return false;}low[cur] = Math.min(low[cur], low[neighbor]);} else if (isInStack[neighbor]) { // 在栈里low[cur] = Math.min(low[cur], dfn[neighbor]);}}int connectedComponentNodeNum = 0;// 若dfn==low,则当前已找到一个强连通分量,该分量节点为当前节点到栈顶的所有节点if (dfn[cur] == low[cur]) {while (cur != stack.peek()) { // 将所有非当前节点退栈int tmp = stack.pop();isInStack[tmp] = false;if (++connectedComponentNodeNum > skill) {return false;}}// 把当前节点退栈stack.pop();isInStack[cur] = false;if (++connectedComponentNodeNum > skill) {return false;}}return true;}
}

3. kosaraju算法

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;public class KosarajuSolution {// 图的邻接表表示形式,记录每个节点是哪些节点的前置节点private List<Integer>[] neighbors;// 图的逆邻接表表示形式,记录每个节点是哪些节点的后置节点private List<Integer>[] rneighbors;private int skill;private boolean[] visited;private Stack<Integer> stack;public boolean scheduleCourses(int[][] prerequisites, int skill) {if (skill < 1) {return false;}this.skill = skill;// 初始化相关存储结构initGraph(prerequisites);// 最大连通分量的节点数return kosaraju();}private void initGraph(int[][] prerequisites) {neighbors = new List[prerequisites.length]; rneighbors = new List[prerequisites.length]; for (int i = 0; i < prerequisites.length; i++) {neighbors[i] = new LinkedList<>();rneighbors[i] = new LinkedList<>();}for (int i = 0; i < prerequisites.length; i++) {for (int to : prerequisites[i]) {neighbors[to].add(i);rneighbors[i].add(to);}}}private boolean kosaraju() {this.visited = new boolean[neighbors.length];this.stack = new Stack<Integer>();for (int i = 0; i < neighbors.length; ++i) { // 遍历正向图,记录出栈顺序if (!this.visited[i]) {kosarajuDfs1(i);}}while (!stack.isEmpty()) { // 从出栈最晚的节点开始,dfs遍历反向图int cur = stack.pop();if (this.visited[cur]) {if (kosarajuDfs2(cur) > skill) // 提前结束return false;}}return true;}private void kosarajuDfs1(int cur) {this.visited[cur] = true;for (int next: this.neighbors[cur]) {if (!this.visited[next]) {kosarajuDfs1(next);}}stack.push(cur);}private int kosarajuDfs2(int cur) {this.visited[cur] = false;int count = 1;for (int pre: this.rneighbors[cur]) {if (this.visited[pre]) {if (count > this.skill) return count; // 提前结束count += kosarajuDfs2(pre);}}return count;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux——基本指令(二)

​ 个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C语言小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 写在前面&#xff1a; 紧接上一章&#xff0c;我们在理解接下来的命令之前&#xff0c…

nodejs微信小程序+python+PHP的外卖数据分析-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

TCP一对一聊天

客户端 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io…

Appium 自动化自学篇 —— 初识Appium自动化!

Appium 简介 随着移动终端的普及&#xff0c;手机应用越来越多&#xff0c;也越来越重要。而作为测试 的我们也要与时俱进&#xff0c;努力学习手机 App 的相关测试&#xff0c;文章将介绍手机自动化测试框架 Appium 。 那究竟什么是 Appium 呢? 接下来我们一起来学习PythonS…

鸿蒙开发之状态管理@State

1、视图数据双向绑定 鸿蒙开发采用的声明式UI&#xff0c;利用状态驱动UI的更新。其中State被称作装饰器&#xff0c;是一种状态管理的方式。 状态&#xff1a;指的是被装饰器装饰的驱动视图更新的数据。 视图&#xff1a;是指用户看到的UI渲染出来的界面。 之所以成为双向…

基于Python+WaveNet+MFCC+Tensorflow智能方言分类—深度学习算法应用(含全部工程源码)(四)

目录 前言引言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据预处理2. 模型构建3. 模型训练及保存4. 模型生成 系统测试1. 训练准确率2. 测试效果 相关其它博客工程源代码下载其它资料下载 前言 博主前段时间发布了一篇有关方言识别和分类模型训练的博客&#xff…

Ubuntu部署EMQX开源版MQTT服务器-Orange Pi部署-服务器部署

一、前言 作为全球最具扩展性的 MQTT 消息服务器&#xff0c;EMQX 提供了高效可靠海量物联网设备连接&#xff0c;能够高性能实时移动与处理消息和事件流数据&#xff0c;本文将介绍如何在Ubuntu 22.04上部署MQTT服务器。我们本次选择开源版&#xff0c;使用离线安装方式部署。…

【Amis Low Code 结合FastAPI进行前端框架开发】

官方文档 封装思想 直接复制官网json数据即可开发每个json中的接口由fastapi 转发&#xff08;透传&#xff09;使其开发模式与前端思维一致 基础组件 from amis import Page, Service, App from pydantic import BaseModel, Field from fastapi import FastAPI, Request, …

智能优化算法应用:基于鸡群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鸡群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鸡群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鸡群算法4.实验参数设定5.算法结果6.参考文献7.MA…

单元测试技术

文章目录 一、单元测试快速入门二、单元测试断言三、Junit框架的常用注解 一、单元测试快速入门 所谓单元测试&#xff0c;就是针对最小的功能单元&#xff0c;编写测试代码对其进行正确性测试。 常规的例如如果在main中测试&#xff0c;比如说我们写了一个学生管理系统&…

MySQL进阶(MySQL学习笔记)

接上回MySQL基础篇 数据完整性约束 定义完整性约束 实体完整性 主键约束 &#xff08;1&#xff09;作为列的完整性约束 &#xff08;2&#xff09;作为表的完整性约束 2.候选键约束 将id字段和user字段设置为候选键 参照完整性 将classid字段设置为外键 用户定义完整性…

OpenVINS学习2——VIRAL数据集eee01.bag运行

前言 周末休息了两天&#xff0c;接着做上周五那个VIRAL数据集没有运行成功的工作。现在的最新OpenVINS需要重新写配置文件&#xff0c;不像之前那样都写在launch里&#xff0c;因此需要根据数据集情况配置好estimator_config.yaml还有两个标定参数文件。 VIRAL数据集 VIRAL…

网格中的最小路径代价

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 问题描述 给你一个下标从 0 开始的整数矩阵 grid &#xff0c;矩阵大小为 m x n &#xff0c;由从 0 …

【WebRTC】【Unity】Unity Web RTC1-Unity中简单实现远程画面

【项目资源下载】 本篇配套直接打开可用的项目包地址&#xff0c;欢迎下载&#xff1a; https://download.csdn.net/download/weixin_41697242/88612084 【背景】 想要在Unity中实现实时远程桌面&#xff0c;找到了Render Streaming这个手段&#xff0c;本篇介绍相应的使用方…

XSS漏洞 深度解析 XSS_labs靶场

XSS漏洞 深度解析 XSS_labs靶场 0x01 简介 XSS原名为Cross-site Sciprting(跨站脚本攻击)&#xff0c;因简写与层叠样式表(Cascading style sheets)重名&#xff0c;为了区分所以取名为XSS。 这个漏洞主要存在于HTML页面中进行动态渲染输出的参数中&#xff0c;利用了脚本语…

【项目小结】优点分析

一、 个人博客系统 一&#xff09;限制强制登录 问题&#xff1a;限制用户登录后才能进行相关操作解决&#xff1a; 1&#xff09;前端&#xff1a; ① 写一个函数用于判断登录状态&#xff0c;如果返回的状态码是200就不进行任何操作&#xff0c;否则Ajax实现页面的跳转操作…

2023/12/12作业

思维导图 作业&#xff1a; 成果图 代码 #include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { speechernew QTextToSpeech(this); ui->setupUi(this); //一直获取当前时间 idst…

如何通过上下滑动实现亮度和音量调节(ArkUI)

场景说明 在音视频应用中通常可以通过上下滑动来调节屏幕亮度和音量大小&#xff0c;本例即为大家介绍如何实现上述UI效果。 说明&#xff1a; 由于当前亮度和音量调节功能仅对系统应用开发&#xff0c;所以本例仅讲解UI效果的实现。 效果呈现 本例效果如下&#xff1a; 当在…

k8s-service 7

由控制器来完成集群的工作负载&#xff0c;service&#xff08;微服务&#xff09;是将工作负载的应用暴露出去&#xff0c;从而解决访问问题 作用&#xff1a;无论是在集群内还是集群外&#xff0c;都可以访问pod上的应用&#xff0c;其实现对集群内的应用pod自动发现和负载均…

关于核心转储和GDB调试的理解

Linux应用程序发生Segmentation fault段错误时&#xff0c;如何利用core dump文件定位错误呢&#xff1f; 在 Linux 系统中&#xff0c;常将“主内存”称为核心(core)&#xff0c;而核心映像(core image) 就是 “进程”(process)执行当时的内存内容。当进程发生错误或收到“信…