图论——环检测
环检测以及拓扑排序
- 前言
- 复习模版
- 环检测-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…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...