当前位置: 首页 > news >正文

【搜索回溯算法篇】:拓宽算法视野--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.解决拓扑排序的原理

  1. 入度的概念

    • 对于有向图中的每个顶点,我们定义其入度为指向该顶点的边的数量。例如,在有向图中,如果顶点 v v v 有三条边指向它,那么顶点 v v v 的入度为3。
  2. 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,就将其加入队列。
    • 重复上述步骤,直到队列为空。
  3. 正确性证明

    • 由于我们首先选择入度为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如何解决拓扑排序问题

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索&#xff08;BFS&#xff09;解决拓扑排…...

计算机网络 (61)移动IP

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

Elasticsearch+kibana安装(简单易上手)

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

音视频多媒体编解码器基础-codec

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

【算法与数据结构】动态规划

目录 基本概念 最长递增子序列&#xff08;中等&#xff09; 最大子数组和&#xff08;中等&#xff09; 基本概念 重叠子问题 一个问题可以被分解为多个子问题&#xff0c;并且这些子问题在求解过程中会被多次重复计算。例如&#xff0c;在计算斐波那契数列时&#xff0c;…...

DeepSeekMoE:迈向混合专家语言模型的终极专业化

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

什么是Maxscript?为什么要学习Maxscript?

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

HyperLogLog 近似累计去重技术解析:大数据场景下的高效基数统计

目录 引言 一、HyperLogLog 核心原理 1.1 算法思想 1.2 误差特性 二、SQL 实现详解(PostgreSQL 示例)...

LabVIEW透镜多参数自动检测系统

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

MySQL数据库(二)- SQL

目录 ​编辑 一 DDL (一 数据库操作 1 查询-数据库&#xff08;所有/当前&#xff09; 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注意力&#xff0c;旨在同时捕捉图像中的高频和低频特征。该机制通过将…...

python 使用Whisper模型进行语音翻译

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

C# Winform enter键怎么去关联button

1.关联按钮上的Key事件按钮上的keypress&#xff0c;keydown&#xff0c;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 相关命令使用方法研究

这个也是一种协议类型&#xff1a; 14.16 使用方法举例 根据之前多种类似的协议的相关信息&#xff1a; HTTP/HTTPS&#xff1a;超文本传输协议&#xff08;HTTP&#xff09;用于Web数据的传输&#xff0c;而HTTPS是HTTP的安全版本&#xff0c;使用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工具&#xff0c;结合了自然语言处理和深度学习技术&#xff0c;能够完成多种任务&#xff0c;如知识问答、数据分析、文案创作、代码开发等。以下将从使用技巧、核心功能及注意事项等方面详细介绍DeepSeek的使用方法…...

19 压测和常用的接口优化方案

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

AI应用部署——streamlit

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

NLP自然语言处理通识

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

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...