图论——环检测
环检测以及拓扑排序
- 前言
- 复习模版
- 环检测-DFS版本
- 环检测- BFS版本
前言
我觉得学习这些之前,一定要对图的数据结构和抽象模型有概念,并且图构建的代码模版应该手到擒来,不然还是挺折磨的,不是这差一点就是那差一点,写道力扣卡卡的非常烦人.
复习模版
我觉得单拿出来再说这个模版一点也不冗余,一定要背会死记住!并且要理解它们的数据结构
- 邻接表形式存储图
返回值类型,参数有哪些,如何构建边的关系,非常重要.
既然是构建图,那么我们的返回值一定是图没毛病吧,我们是用邻接表的形式,所以返回值类型就是List [].
对于参数而言:
a. 我们要确认这个图有几个节点,不然没办法创建节点.
b. 我们要知道节点和边的关系,一般给你的是二维的数据,里面有节点和边的关系
如何构建边的关系呢?
如果给你的是一个二维数组,你看题意是怎么说的.
我拿课程表这个题给你举例子,[0,1]表示:想学习课程0,就要先完成课程1.
指向由你自己选择,我这边选择from是1,to是0.
指向关系是from指向to.
那如何添加到邻接表里,就看你对邻接表数据结构的理解,角码代表节点,里面的值代表这个节点指向哪个节点.
代码实现如下:
List<Integer> buildGraph(int numCourses, int[][] prerequisites){List<Integer>[] graph = new LinkedList[numCourses];for(int i=0;i<numCourses;i++){graph[i] = new LinkedList<>();}for(int[] edge: prerequisites){int from = edge[1],to = edge[0];graph[from].add(to);}
}
环检测-DFS版本
构建图的模版我就不写了,上面是有的.
上一章我们聊过图论中和树不一样的地方在于图可能是会有环的.
不做任何处理的话可能会造成死循环.
处理的方式就是记录下来我哪些节点已经遍历过了,如果已经遍历了就不能再遍历,并标记是有环了.
那么我们需要两个小伙伴帮我们记录:
- boolean[] onPath - 记录递归遍历过哪些节点了
- boolean hasCycle - 图中是否有环
应该很好理解吧,代码如下:
class Solution {// 记录递归堆栈中的节点boolean[] onPath;// 记录图中是否有环boolean hasCycle = false;public boolean canFinish(int numCourses, int[][] prerequisites) {List<Integer>[] graph = buildGraph(numCourses, prerequisites);onPath = new boolean[numCourses];for (int i = 0; i < numCourses; i++) {// 遍历图中的所有节点traverse(graph, i);}// 只要没有循环依赖可以完成所有课程return !hasCycle;}// 图遍历函数,遍历所有路径void traverse(List<Integer>[] graph, int s) {if (hasCycle) {// 如果已经找到了环,也不用再遍历了return;}if (onPath[s]) {// s 已经在递归路径上,说明成环了hasCycle = true;return;}// 前序代码位置onPath[s] = true;for (int t : graph[s]) {traverse(graph, t);}// 后序代码位置onPath[s] = false;}List<Integer>[] buildGraph(int numCourses, int[][] prerequisites) {// 代码见前文}
}
关于上面遍历代码我想补充几点:
- 我们是要对每个节点都进行递归遍历,而不是只遍历一次,我刚开始学图的时候老是忘记处理这点.因为和树不一样,树只有一个起点,但是图可能有多个连通分量,而树只有一个!
- onPath数组的处理要理解代表的含义,在onPath的角码就是代表哪个节点.onPath[i]的意思就是i节点是否成环.
但是上面是有冗余计算的,假如我以3节点为起点遍历了所有可达路径,没有发现环,那么我又以5为起点,遍历到3节点,我还是要再去遍历一遍3节点.这就非常难受了.
所以我们不妨再找一个小伙伴帮忙记一下,让我们知道已经遍历过哪些节点,就不需要再遍历了
boolean[] visited 就是干这个的.
visited[i] 的意义是i节点是否被遍历过.
很简单吧,代码如下:
class Solution {// 记录一次递归堆栈中的节点boolean[] onPath;// 记录节点是否被遍历过boolean[] visited;// 记录图中是否有环boolean hasCycle = false;public boolean canFinish(int numCourses, int[][] prerequisites) {List<Integer>[] graph = buildGraph(numCourses, prerequisites);onPath = new boolean[numCourses];visited = new boolean[numCourses];for (int i = 0; i < numCourses; i++) {// 遍历图中的所有节点traverse(graph, i);}// 只要没有循环依赖可以完成所有课程return !hasCycle;}// 图遍历函数,遍历所有路径void traverse(List<Integer>[] graph, int s) {if (hasCycle) {// 如果已经找到了环,也不用再遍历了return;}if (onPath[s]) {// s 已经在递归路径上,说明成环了hasCycle = true;return;}if (visited[s]) {// 不用再重复遍历已遍历过的节点return;}// 前序代码位置visited[s] = true;onPath[s] = true;for (int t : graph[s]) {traverse(graph, t);}// 后序代码位置onPath[s] = false;}List<Integer>[] buildGraph(int numCourses, int[][] prerequisites) {// 代码见前文}
}
环检测- BFS版本
DFS是通过数组来判定是否成环,但是BFS不能这样了,因为它没办法撤回,只能往下走.
那我们可以通过每个节点的入度来实现.
所谓「出度」和「入度」是「有向图」中的概念,很直观:如果一个节点 x 有 a 条边指向别的节点,同时被 b 条边所指,则称节点 x 的出度为 a,入度为 b。
那既然说我们依赖节点的入度,它一定是被事先构建好的,所以我们除了要写一个构建图的代码,还要再写一段构建入度的代码.
上面也说了,判定入度是根据边的指向.
那么我们肯定是要循环一遍我们构建的图,然后根据节点的边去设置每个节点的入度.
代码如下:
int[] indegree = new int[numCourses];for(int[] edge: prerequisites){int from = edge[1], to = edge[0];//注意这里计算的是入度,所以脚码是toindegree[to]++;
}
因为是BFS,所以我们用队列去处理,那这个队列的话我们如何去管理呢?
首先我们肯定要往队列里面添加初始节点,我们怎么判断哪个节点是初始节点?
记住我们的核心出装: 根据入度来判定,入度>0,则代表它是中间节点;入度=0,代表是初始节点.所以我们选择遍历到入度为0 的节点添加到队列里面.
Queue<Integer> q = new LinkedList<>();
for(int i =0; i<numCourses;i++){if(indegree[i] == 0){q.offer(i);}
}
遍历如何处理呢?
✅ 终止条件: 队列为空时,表示所有可以遍历的节点都已经处理完,循环结束。
✅ 遍历方式: 每次从队列中弹出一个入度为 0 的节点,遍历它能指向的所有节点,并更新入度。
✅ 入度变为 0 时入队: 说明当前节点的所有前置依赖都已被处理完,可以加入队列,等待后续遍历。
while(!q.isEmpty()){int cur = q.poll();for(int next: graph[cur]){indegree[next]--;if(indegree[next]==0{q.offer(next);}}
}
遍历完后,那我怎么知道是不是有环呢?
我们思考一下,如果有环的话.在环中的入度可能为0吗?
我们想一下,从正常的节点,遍历到有环的节点,有环的节点会被添加进去吗?
答案是不会的,因为有环的话我们无法消除这个节点的入度呀.(不理解的简单画个图就理解了,很简单的道理)
我一个节点去遍历到下一个节点,只能把下一个节点的度-1,但你想想这个-1减的是谁的1,是下一个环对上一个环的依赖,不是下一个环对下下一个环的依赖!
所以有环的节点不会被加入进去,那么我们在遍历的时候就会少遍历一个.
所以所以!
我们就记录一下遍历的节点个数,和最后的节点比对一下,如果不相等,那么就代表有环!
ok,整个流程就是这样,代码如下,细细品味吧
class Solution {public boolean canFinish(int numCourses, int[][] prerequisites) {// 建图,有向边代表「被依赖」关系List<Integer>[] graph = buildGraph(numCourses, prerequisites);// 构建入度数组int[] indegree = new int[numCourses];for (int[] edge : prerequisites) {int from = edge[1], to = edge[0];// 节点 to 的入度加一indegree[to]++;}// 根据入度初始化队列中的节点Queue<Integer> q = new LinkedList<>();for (int i = 0; i < numCourses; i++) {if (indegree[i] == 0) {// 节点 i 没有入度,即没有依赖的节点// 可以作为拓扑排序的起点,加入队列q.offer(i);}}// 记录遍历的节点个数int count = 0;// 开始执行 BFS 循环while (!q.isEmpty()) {// 弹出节点 cur,并将它指向的节点的入度减一int cur = q.poll();count++;for (int next : graph[cur]) {indegree[next]--;if (indegree[next] == 0) {// 如果入度变为 0,说明 next 依赖的节点都已被遍历q.offer(next);}}}// 如果所有节点都被遍历过,说明不成环return count == numCourses;}// 建图函数List<Integer>[] buildGraph(int n, int[][] edges) {// 见前文}
}
相关文章:
图论——环检测
环检测以及拓扑排序 前言复习模版环检测-DFS版本环检测- BFS版本 前言 我觉得学习这些之前,一定要对图的数据结构和抽象模型有概念,并且图构建的代码模版应该手到擒来,不然还是挺折磨的,不是这差一点就是那差一点,写道力扣卡卡的非常烦人. 复习模版 我觉得单拿出来再说这个模…...
Chapter2:C#基本数据类型
参考书籍:《C#边做边学》; 2.C#基本数据类型 2.1 变量与常量 变量是程序运行过程中用于存放数据的存储单元,变量的值的程序运行过程中可以改变; 变量定义: 定义变量时,必须给每个变量起名,通过…...
kafka服务端之控制器
文章目录 概述控制器的选举与故障恢复控制器的选举故障恢复 优雅关闭分区leader的选举 概述 在Kafka集群中会有一个或多个broker,其中有一个broker会被选举为控制器(Kafka Controler),它负责管理整个集群中所有分区和副本的状态。…...
Unity笔试常考
线程同步的几种方式 1.信号量pv操作 2.互斥加锁 3.条件变量 五层网络协议指的是哪五层 1.应用层 2.运输层 3.网络层 4.链路层 5.物理层 TCP和UDP区别 tcp 面向连接,保证发送顺序,速度慢,必须在线,三次握手,4次挥手…...
移植BOA服务器到GEC2440开发板
所需软件:boa-0.94.13.tar.tar(下载:http://www.boa.org/boa-0.94.13.tar.gz) 步骤: 设置好交叉编译工具链。 1、解压下载好的压缩包(tar xzvf boa-0.94.13.tar.tar),并进入解压后的目录(cd boa-0.94.13),再进行如下操作: 先进入到src目录(下面操作都是在该目录下进行…...
WPS如何接入DeepSeek(通过第三方工具)
WPS如何接入DeepSeek 一、下载并安装OfficeAI插件二、配置OfficeAI插件三、使用DeepSeek功能 本文介绍如何通过 WPS 的第三方工具调用 DeepSeek 大模型,实现自动化文本扩写、校对和翻译等功能。 一、下载并安装OfficeAI插件 1、访问OfficeAI插件下载地址ÿ…...
【安当产品应用案例100集】037-强化OpenVPN安全防线的卓越之选——安当ASP身份认证系统
在当前数字化时代,网络安全已成为企业发展的重要组成部分。对于使用OpenVPN的企业而言,确保远程访问的安全性尤为重要。安当ASP身份认证系统凭借其强大的功能和便捷的集成方式,为OpenVPN的二次登录认证提供了理想的解决方案,特别是…...
Windows Docker笔记-制作、加载镜像
引言 在文章《Windows Docker笔记-在容器中运行项目》中,已经在容器中运行了项目。而且在这个容器中,已经调试好了项目运行的环境。 使用docker,就是为了在项目发布到生产环境时,不用再去安装项目运行的环境,直接丢给…...
leetcode_26删除有序数组中的重复项
1. 题意 给定一个重复数组,删除其中的重复项目。 2. 题解 双指针 一个指针指向有序不重复数组的最后一个数,另外一个数遍历整个数组,若两个指针对应用的数不相同,有序数组的指针右移,将数填入。 代码一 class Sol…...
速递丨DeepSeek刚刚成立香港子公司,或因考虑香港上市和招募全球AI人才
图片来源:DeepSeek 根据彭博社和财联社报道,DeepSeek 2月5日在香港成立了两家公司——DeepSeek Limited 和 DeepSeek (HK) Limited。 香港中文大学莊太量教授表示,DeepSeek进军香港将推动该市的金融科技发展。如果DeepSeek考虑在香港上市&a…...
笔灵ai写作技术浅析(六):智能改写与续写
笔灵AI写作中的智能改写和续写技术是其核心功能之一,旨在帮助用户生成高质量、多样化的文本内容。 一、智能改写技术 1. 基本原理 智能改写的目标是在保持原文语义不变的前提下,对文本进行重新表述,生成语法正确、语义连贯且风格多样的新文本。其核心思想是通过语义理解和…...
【在线优化】【有源程序】基于遗传算法(GA)和粒子群优化(PSO)算法的MPPT控制策略
目录 一、背景 二、源程序及结果 2.1 simulink仿真程序 2.2 GA模块源程序 2.3 PSO模块源程序 三、程序运行结果 3.1 基于GA优化的MPPT 3.2 基于PSO优化的MPPT 一、背景 MPPT策略能够显著提高光伏、风电等发电效率,节省大量成本。该策略的经典算法是…...
使用 Three.js 实现热力渐变效果
大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:前端开发 | A…...
java-异常家族梳理(流程图)
前言: 使用流程图梳理异常,便于理解 梳理: Throwable ├── Error(严重错误,无需捕获) │ ├── OutOfMemoryError │ ├── StackOverflowError │ └── ... ├── Exception(可捕获处理) │ ├── RuntimeException(非检查异常/Unchecked) │ …...
开启蓝耘之旅:DeepSeek R1 模型在智算平台的起步教程
----------------------------------------------------------我的个人主页-------------------- 动动你的手指----------------------------------------点赞👍 收藏❤--------------------------------------------------------------- 引言 在深度学习的广袤领…...
[高等数学]不定积分的概念与性质
一、知识点 (一)原函数与不定积分的概念 定义1(原函数) 如果在区间 I I I 上,可导函数 F ( x ) F(x) F(x) 的导函数为 f ( x ) f(x) f(x),即对任一 x ∈ I x\in I x∈I,都有 F ′ ( x )…...
【算法】【高精度】acwing算法基础 793. 高精度乘法
题目 给定两个非负整数(不含前导 0) A 和 B,请你计算 AB 的值。 输入格式 共两行,第一行包含整数 A,第二行包含整数 B。 输出格式 共一行,包含 AB 的值。 数据范围 1≤A的长度≤100000, 0≤B≤10000 输入样…...
sqlite 查看表结构
在SQLite中,查看表结构通常有以下几种方法: 使用.schema命令 在SQLite的命令行界面中,你可以使用.schema命令加上表名来查看该表的结构。例如,如果你想查看名为your_table_name的表结构,你可以这样做: .s…...
测试中的第一性原理:回归本质的质量思维革命
在软件工程领域,测试活动常被惯性思维和经验主义所主导——测试用例库无限膨胀、自动化脚本维护成本居高不下、测试策略与业务目标渐行渐远。要突破这种困境,第一性原理(First Principles Thinking)提供了独特的解题视角ÿ…...
flink判断两个事件之间有没有超时(不使用CEP)
1.为啥不使用cep呢,cep的超时时间设置不好配置化,无法满足扩展要求 2.超时怎么界定。A事件发生后,过了N时间,还没有收到B事件,算超时。 代码如下: import com.alibaba.fastjson.JSONObject; import lombo…...
DeepSeek代码质量评估实战手册:7步完成从混沌到可度量的质变跃迁
更多请点击: https://kaifayun.com 第一章:DeepSeek代码质量评估的底层逻辑与核心价值 DeepSeek代码质量评估并非简单地统计行数或检测语法错误,而是基于多维语义理解构建的推理系统。其底层逻辑融合了静态分析、符号执行与大语言模型生成式…...
【DeepSeek测试用例生成实战指南】:20年QA专家亲授5大高覆盖率生成模式与3个避坑红线
更多请点击: https://codechina.net 第一章:DeepSeek测试用例生成的核心价值与适用边界 DeepSeek系列大模型在代码理解与生成任务中展现出显著的上下文建模能力,其测试用例生成功能并非通用“黑盒测试器”,而是聚焦于**单元级、函…...
Python合并Excel文档
有若干个Excel文档,每个文档格式一致,及第一行为文件标题,第二行为表格表头(表头不完全一致)。现需要将他们合并。合并规则为:去掉每个文档的第一行,以第二行为表头,将每个文档的第三…...
Unity发行版DLL调试实战:DnSpy无源码IL级断点指南
1. 这不是“反编译”,而是Unity游戏开发者的日常调试手段你有没有遇到过这样的情况:接手一个Unity发行版游戏,想快速验证某个功能逻辑是否按预期执行,或者排查一个偶发的崩溃,但手头只有打包后的Assembly-CSharp.dll&a…...
光轮智能 谢晨 访谈总结机器人仿真数据产业
光轮智能 谢晨 访谈总结机器人仿真关于创始人关于数据数据金字塔数据痛点仿真数据的重要性仿真数据的质量b站链接地址公司官网关于创始人 清华物理;哥伦比亚金融;英伟达智驾仿真;小鹏智驾仿真;现为光轮智能CEO 关于数据 数据的…...
基于MAX78000的医疗紧急呼叫系统:边缘AI与低功耗设计实战
1. 项目概述与核心价值大家好,我是Victor Hugo,一名电子工程师。今天我想和大家分享一个我最近完成并参与设计竞赛的项目:一个基于MAX78000 FTHR开发板的医疗紧急呼叫辅助系统。这个项目的核心,不是从零开始造一个新轮子ÿ…...
双稳健机器学习:用正交性与交叉拟合解决因果推断中的ML偏差
1. 项目概述:当机器学习遇见因果推断的“干扰”难题在实证研究的日常工作中,我们常常面临一个核心矛盾:我们真正关心的,往往只是一个或几个关键参数——比如一项政策对就业率的平均影响(平均处理效应,ATE&a…...
如何快速集成 react-native-bottom-sheet-behavior:5 分钟搞定 Android 底部弹窗
如何快速集成 react-native-bottom-sheet-behavior:5 分钟搞定 Android 底部弹窗 【免费下载链接】react-native-bottom-sheet-behavior react-native wrapper for android BottomSheetBehavior 项目地址: https://gitcode.com/gh_mirrors/re/react-native-bottom…...
人工智能的伦理与安全:这3个问题,软件测试从业者必须重视
随着大语言模型、生成式AI的爆发式落地,人工智能已经从实验室走向千行百业的生产场景,深刻改变着软件开发与交付的逻辑。对于直接把控产品质量关口的软件测试从业者来说,我们的职责早已不再是单纯验证功能可用性、排查性能bug那么简单——AI系…...
告别Windows卡顿!在VMware里给Kubuntu 22.04 LTS分区和安装的保姆级避坑指南
告别Windows卡顿!在VMware里给Kubuntu 22.04 LTS分区和安装的保姆级避坑指南你是否已经厌倦了Windows系统越来越慢的启动速度、频繁的后台更新和资源占用?当你的电脑开始频繁卡顿,或许该考虑给系统来一次"减负"了。Kubuntu 22.04 L…...
