【搜索回溯算法篇】:拓宽算法视野--BFS如何解决拓扑排序问题
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:搜索回溯算法篇–CSDN博客
文章目录
- 一.广度优先搜索(BFS)解决拓扑排序
- 1.拓扑排序简介
- 2.解决拓扑排序的原理
- 二.例题
- 1.课程表
- 2.课程表||
- 3.火星词典
一.广度优先搜索(BFS)解决拓扑排序
1.拓扑排序简介
拓扑排序是对有向无环图(DAG, Directed Acyclic Graph)的顶点的一种排序,使得如果存在一条从顶点 u u u 到顶点 v v v 的有向边 ( u , v ) (u, v) (u,v),那么在排序中 u u u 出现在 v v v 之前。它在许多实际应用场景中有重要作用,例如任务调度(每个任务有前置任务要求)、课程安排(先修课程的顺序安排)等。
2.解决拓扑排序的原理
-
入度的概念
- 对于有向图中的每个顶点,我们定义其入度为指向该顶点的边的数量。例如,在有向图中,如果顶点 v v v 有三条边指向它,那么顶点 v v v 的入度为3。
-
BFS - 基于入度的操作
- 首先,计算图中每个顶点的入度。
- 将所有入度为0的顶点放入队列中。这些顶点是可以作为拓扑排序的起始点,因为没有边指向它们,也就没有前置的依赖关系。
- 然后开始进行BFS:
- 从队列中取出一个顶点 u u u,将其加入到拓扑排序的结果序列中。
- 对于顶点 u u u 的所有邻接顶点 v v v,将它们的入度减1(因为 u u u 到 v v v 的边被“处理”了,相当于减少了 v v v 的一个入度来源)。
- 如果某个邻接顶点 v v v 的入度变为0,就将其加入队列。
- 重复上述步骤,直到队列为空。
-
正确性证明
- 由于我们首先选择入度为0的顶点加入队列,这些顶点没有前置依赖,可以首先出现在拓扑排序结果中。
- 当我们处理一个顶点并减少其邻接顶点的入度时,实际上是在逐步消除依赖关系。当一个顶点的入度变为0时,说明它之前的所有依赖都已经被处理过了,所以可以将其加入队列并放入拓扑排序结果中。
- 因为图是有向无环图,所以这个过程最终会处理完所有顶点,得到正确的拓扑排序结果。
二.例题
1.课程表
题目:
算法原理:
首先明白题意要求,给定一个二维数组,每一行中存储的是一对数字,比如示例一(1,0),表示学习课程1之前需要先学习课程0,用有向图表示就是0->1;要求判断给定的所有数字对是否能完成所有课程的学习;
本道题的重点就是如何使用拓扑排序来判断,但是在拓扑排序之前必须是有向图才可以拓扑排序,所以需要先根据给定的数组来建立有向图;建图通常借助两个哈希表或者数组来实现,一个用来表示指向关系,比如a->b;一个用来表示每个节点的入度个数;
在本道题中,因为课程是用数字来表示,正好对应下标,所以可以用数组来实现,这里我写的是用哈希表来表示指向关系,用数组来表示入度个数,具体可以看代码中的注释。
代码实现:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites){// 用哈希表来表示邻接表// key值表示一个节点,val表示一个数组,里面存放的是key值节点指向的下一个节点// key=0;val=[1,2,3];表示0指向1,2,3三个节点unordered_map<int, vector<int>> edges;//用数组来存放每个节点的入度,本道题中下标正好对应节点vector<int> in(numCourses);//建图for(auto& nums : prerequisites){//[a,b]表示b->a,在完成a之前先完成bint a = nums[0], b = nums[1];//b->a,存放b的下一个节点edges[b].push_back(a);//节点a入度+1in[a]++;}//将入度为0的入队queue<int> q;for (int i = 0; i < in.size(); i++){if(in[i]==0){q.push(i);}}//拓扑排序while(!q.empty()){//获取队头节点并出队int t = q.front();q.pop();//遍历当前下标对应的所有下一个节点,将对应的节点入度-1,表示删除指向的边for(auto num : edges[t]){in[num]--;//如果对应节点的入度为0,入队if(in[num]==0){q.push(num);}}}//遍历入度数组,如果出现某个节点的入度不为0说明存在环,不能遍历所有节点for (auto num : in){if(num==1){return false;}}return true;
}
2.课程表||
题目:
算法原理:
本道题和上面一道题可以说是一模一样,只不过是如果可以完成所有的课程,就输出课程顺序,所以在上一道题的基础上在bfs实现拓扑排序时,只需要将每个出队的头节点存放到数组中即可,因为出队顺序就是拓扑排序的顺序。
代码实现:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites){//本道题是上一道的变形,具体过程一模一样//用哈希表表示邻接表,数组存放每个节点的入度unordered_map<int, vector<int>> edges;vector<int> in(numCourses);//建表for(auto& e : prerequisites){//b->aint a = e[0], b = e[1];edges[b].push_back(a);in[a]++;}//将所有入度为0的入队queue<int> q;for (int i = 0; i < in.size(); i++){if(in[i]==0){q.push(i);}}//建立一个数组用来返回最终的结果vector<int> ret;//拓扑排序,bfs实现while(!q.empty()){//获取队头节点并出队int t=q.front();q.pop();ret.push_back(t);//通过当前节点遍历所有指向的节点for(auto num : edges[t]){//修改指向的节点的入度in[num]--;//如果入度为0,入队if(in[num]==0){q.push(num);}}}//遍历入度数组,如果出现某个节点的入度不为0,说明存在环,返回空数组for(auto num : in){if(num!=0){ret.clear();return ret;}}return ret;
}
3.火星词典
题目:
算法原理:
本道题其实也是根据拓扑排序的原理来解决,题意要求根据词典(words数组)来找到每个字母的先后顺序,然后返回正确的字母顺序;比如,示例一中的"wrt"和"wrf"因为前两个字母是一样的,而在第三个字母出现不同,但又因为"wrt"出现在"wrf"前面,所以字母"t"的顺序在字母"f"的前面,对应题意中的第一种情况;还有就是"abc"和"abcde",因为第一个字符串的长度小于第二个字符串,但是前三个字符又正好相同,没有找到不相同的字符,如果是这种情况,输出的字母顺序那就是字母"de"的顺序在前,然后"abc"三个字母的顺序随便,因为无法判断出这三个字母的顺序;但是可能会出现这种情况第一个字符串是"abcde"而第二个字符串是"abc",这种情况就是违反规则,直接返回空串即可。
所以实现过程还是先根据给定的信息建立有向图,然后拓扑排序,获取信息其实就是将给定的词典数组,两两字符串进行比较获取每个字母的顺序关系,根据上面的两种情况来获取信息。具体的过程注释可以看代码中写的。
代码实现:
string alienOrder(vector<string>& words){// 建立一个边哈希表,key值表示字符,val值表示哈希表用来存放该字符指向的所有字符// 因为查找该字符的指向字符时,可能会重复出现,所以内层哈希表用set型去重// 例:key=t;val=[d,c,a];表示t->d&&t->c&&t->a;unordered_map<char, unordered_set<char>> edges;//建立一个入度哈希表,用来存放每个字符的入度//key值表示字符,val值表示该字符对应的入度unordered_map<char, int> in;//先遍历整个词典,将所有出现的字符存放到入度哈希表中并将入度初始化为0,防止后面某些字符没有遍历到for(auto s : words){for(auto ch : s){if(in.count(ch)==0){in[ch] = 0;}}}//两层for循环遍历词典,建AOV图for (int i = 0; i < words.size(); i++){string s1 = words[i];for (int j = i + 1; j < words.size(); j++){string s2 = words[j];//两个指针,一个指向第一个字符串中的起始位置,一个指向第二个字符串的起始位置int cur1 = 0, cur2 = 0;while(cur1<s1.size()&&cur2<s2.size()){//如果两个指针指向对应字符串中的字符不相同,表示s1[cur1]->s2[cur2]//s1的字符指向s2的字符,s2的字符入度+1if(s1[cur1]!=s2[cur2]){//这里有一个细节,如果b->a重复出现,a字符的入度就会重复+1//所以要进行一个判断,如果b字符的哈希表中已经存在字符a,直接跳过if(edges[s1[cur1]].count(s2[cur2])==0){edges[s1[cur1]].insert(s2[cur2]);in[s2[cur2]]++;}//找到第一对不相同的字符就结束break;}else{cur1++;cur2++;}}// 如果两个字符串没有找到相同的字符,有两种情况// 1.s1=ab;s2=abc;不用处理// 2.s1=abc;s2=ab;直接返回空串,因为违反规则if(cur2==s2.size()&&cur1<s1.size()){return "";}}}//设置一个结果字符串用来存放字符的顺序string ret;//设置一个队列queue<char> q;//遍历入度哈希表将所有入度为0的字符入队for(auto& [ch,count] : in){if(count==0){q.push(ch);}}//bfs实现拓扑排序while(!q.empty()){//获取队头字符并出队char ch = q.front();q.pop();//结果字符串加上队头字符ret += ch;//找到该字符指向的所有字符,将指向的字符入度-1,表示删除指向的边for(auto& nextch : edges[ch]){in[nextch]--;//如果指向的字符入度减为0,将该字符入队if(in[nextch]==0){q.push(nextch);}}}//遍历入度哈希表,如果出现某个字符的入度不为0,说明顺序错误,不能将所有字符拓扑排序for(auto& [ch,count] : in){if(count!=0){//直接返回空串return "";}}return ret;
}
以上就是关于bfs解决拓扑排序的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
相关文章:

【搜索回溯算法篇】:拓宽算法视野--BFS如何解决拓扑排序问题
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索(BFS)解决拓扑排…...

计算机网络 (61)移动IP
前言 移动IP(Mobile IP)是由Internet工程任务小组(Internet Engineering Task Force,IETF)提出的一个协议,旨在解决移动设备在不同网络间切换时的通信问题,确保移动设备可以在离开原有网络或子网…...

Elasticsearch+kibana安装(简单易上手)
下载ES( Download Elasticsearch | Elastic ) 将ES安装包解压缩 解压后目录如下: 修改ES服务端口(可以不修改) 启动ES 记住这些内容 验证ES是否启动成功 下载kibana( Download Kibana Free | Get Started Now | Elastic ) 解压后的kibana目…...

音视频多媒体编解码器基础-codec
如果要从事编解码多媒体的工作,需要准备哪些更为基础的内容,这里帮你总结完。 因为数据类型不同所以编解码算法不同,分为图像、视频和音频三大类;因为流程不同,可以分为编码和解码两部分;因为编码器实现不…...

【算法与数据结构】动态规划
目录 基本概念 最长递增子序列(中等) 最大子数组和(中等) 基本概念 重叠子问题 一个问题可以被分解为多个子问题,并且这些子问题在求解过程中会被多次重复计算。例如,在计算斐波那契数列时,…...

DeepSeekMoE:迈向混合专家语言模型的终极专业化
一、结论写在前面 论文提出了MoE语言模型的DeepSeekMoE架构,目的是实现终极的专家专业化(expert specialization)。通过细粒度的专家分割和共享专家隔离,DeepSeekMoE相比主流的MoE架构实现了显著更高的专家专业化和性能。从较小的2B参数规模开始&#x…...

什么是Maxscript?为什么要学习Maxscript?
MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…...

HyperLogLog 近似累计去重技术解析:大数据场景下的高效基数统计
目录 引言 一、HyperLogLog 核心原理 1.1 算法思想 1.2 误差特性 二、SQL 实现详解(PostgreSQL 示例)...

LabVIEW透镜多参数自动检测系统
在现代制造业中,提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统,该系统能够在单一工位上完成透镜的多项质量参数检测,并实现透镜的自动搬运与分选,极大地提升了检…...

MySQL数据库(二)- SQL
目录 编辑 一 DDL (一 数据库操作 1 查询-数据库(所有/当前) 2 创建-数据库 3 删除-数据库 4 使用-数据库 (二 表操作 1 创建-表结构 2 查询-所有表结构名称 3 查询-表结构内容 4 查询-建表语句 5 添加-字段名数据类型 6 修改-字段数据类…...

【Block总结】HiLo注意力,局部自注意力捕获细粒度的高频信息,通过全局注意力捕获低频信息|即插即用
一、论文信息 标题: Fast Vision Transformers with HiLo AttentionGitHub链接: https://github.com/ziplab/LITv2论文链接: arXiv 二、创新点 HiLo注意力机制: 本文提出了一种新的自注意力机制——HiLo注意力,旨在同时捕捉图像中的高频和低频特征。该机制通过将…...

python 使用Whisper模型进行语音翻译
目录 一、Whisper 是什么? 二、Whisper 的基本命令行用法 三、代码实践 四、是否保留Token标记 五、翻译长度问题 六、性能分析 一、Whisper 是什么? Whisper 是由 OpenAI 开源的一个自动语音识别(Automatic Speech Recognition, ASR)系统。它的主要特点是: 多语言…...

C# Winform enter键怎么去关联button
1.关联按钮上的Key事件按钮上的keypress,keydown,keyup事件随便一个即可private void textBox1_KeyDown(object sender, KeyEventArgs e){if (e.KeyCode Keys.Enter){this.textBox2.Focus();}}2.窗体上的事件private void textBox2_KeyPress(object sen…...

Github 2025-01-30 Go开源项目日报 Top10
根据Github Trendings的统计,今日(2025-01-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:2724 次关注人…...

电路研究9.2.6——合宙Air780EP中HTTP——HTTP GET 相关命令使用方法研究
这个也是一种协议类型: 14.16 使用方法举例 根据之前多种类似的协议的相关信息: HTTP/HTTPS:超文本传输协议(HTTP)用于Web数据的传输,而HTTPS是HTTP的安全版本,使用SSL/TLS进行加密。与FTP相比&…...

Java手写简单Merkle树
Java手写Merkle树代码 package com.blockchain.qgy.component;import com.blockchain.qgy.model.MerkleTreeNode; import com.blockchain.qgy.util.SHAUtil;import java.util.*;public class MerkleTree<T> {//merkle树private List<MerkleTreeNode<T>> lis…...

DeepSeek的使用技巧介绍
DeepSeek是一款由杭州深度求索人工智能技术有限公司开发的AI工具,结合了自然语言处理和深度学习技术,能够完成多种任务,如知识问答、数据分析、文案创作、代码开发等。以下将从使用技巧、核心功能及注意事项等方面详细介绍DeepSeek的使用方法…...

19 压测和常用的接口优化方案
高并发的平台应用,项目上线前离不开一个重要步骤就是压测,压测对于编码中的资源是否问题的排查,性能的调优都是离不开的。测试还要做测试报告,出具了测试报告给到运维团队才能上线。 压测的测试报告主要有以下几个方面:1.响应时间…...

AI应用部署——streamlit
如何把项目部署到一个具有公网ip地址的服务器上,让他人看到? 可以利用 streamlit 的社区云免费部署 1、生成requirements.txt文件 终端输入pip freeze > requirements.txt即可 requirements.txt里既包括自己安装过的库,也包括这些库的…...

NLP自然语言处理通识
目录 ELMO 一、ELMo的核心设计理念 1. 静态词向量的局限性 2. 动态上下文嵌入的核心思想 3. 层次化特征提取 二、ELMo的模型结构与技术逻辑 1. 双向语言模型(BiLM) 2. 多层LSTM的层次化表示 三、ELMo的运行过程 1. 预训练阶段 2. 下游任务微调 四、ELMo的…...

C++ 6
C构造函数有几种,分别什么作用 在C中,构造函数有几种不同的类型,每种都有其特定的作用: 默认构造函数:没有参数的构造函数,用于创建对象的默认实例。参数化构造函数:带参数的构造函数…...

使用QSqlQueryModel创建交替背景色的表格模型
class UserModel(QSqlQueryModel):def __init__(self):super().__init__()self._query "SELECT name, age FROM users"self.refresh()def refresh(self):self.setQuery(self._query)# 重新定义data()方法def data(self, index, role): if role Qt.BackgroundRole…...

jinfo命令详解
jinfo [option]option 有以下这些选项参数 -flag : 打印 指定名称的 jvm 参数值;-flag [|-] : 启动或禁用指定名称的 jvm参数;-flag : 设置指定名称的 jvm 参数值;-sysprops: 打印 java 系统属性-h | -help: 打印 jinfo 命令帮助信息 1&…...

如何在 ACP 中建模复合罐
概括 本篇博文介绍了 ANSYS Composite PrepPost (ACP) 缠绕向导。此工具允许仅使用几个条目自动定义高压罐中常见的悬垂复合结构。 ACP 绕线向导 将必要的信息输入到绕组向导中。重要的是要注意“参考半径”,它代表圆柱截面的半径,以及“轴向”&#x…...

【Java】微服务找不到问题记录can not find user-service
一、问题描述 运行网关微服务与用户微服务后,nacos服务成功注册 但是测试接口的时候网关没有找到相关服务 二、解决方案 我先检查了pom文件确定没问题后查看配置文件 最后发现是配置里spring.application.namexxx-user里面服务的名字后面多了一个空格 三、总结…...

基于Hutool的Merkle树hash值生成工具
SHAUtil工具 package com.blockchain.qgy.util;import com.xiaoleilu.hutool.crypto.digest.DigestUtil; import org.apache.commons.codec.binary.Hex;import java.nio.charset.StandardCharsets; import java.security.MessageDigest;/**** 生成SHA-256的工具** author QGY*…...

Windows系统本地部署deepseek 更改目录
本地部署deepseek 无论是mac还是windows系统本地部署deepseek或者其他模型的命令和步骤是一样的。 可以看: 本地部署deepsek 无论是ollama还是部署LLM时候都默认是系统磁盘,对于Windows系统,我们一般不把应用放到系统盘(C:)而是…...

深度学习篇---数据存储类型
文章目录 前言第一部分:C语言中的数据存储类型1. char(通常是8位)优点缺点 2. short(通常是16位)优点缺点 3. int(通常是32位)优点缺点 4. long(通常是32位或64位)优点缺…...

可被electron等调用的Qt截图-录屏工具【源码开放】
1. 工具功能简介: (1)、QT5.15.2截图工具(exe)可单独使用或嵌入IM(嵌入方法参照:https://gitee.com/lykiao/yfscreenshot_release) (2)、支持通过Windows消息通知截图成功或取消 (3)、支持圆形、矩形、线条…...

electron 应用开发实践
参考链接: https://blog.csdn.net/2401_83384536/article/details/140549279...