算法沉淀——BFS 解决拓扑排序(leetcode真题剖析)

在这里插入图片描述

算法沉淀——BFS 解决拓扑排序

  • 01.课程表
  • 02.课程表 II
  • 03.火星词典

Breadth-First Search (BFS) 在拓扑排序中的应用主要是用来解决有向无环图(DAG)的拓扑排序问题。拓扑排序是对有向图中所有节点的一种线性排序,使得对于每一条有向边 (u, v),节点 u 在排序中都出现在节点 v 的前面。如果图中存在环路,则无法进行拓扑排序。

BFS 解决拓扑排序的步骤如下:

  1. 统计每个节点的入度(in-degree),即指向该节点的边的数量。
  2. 将所有入度为 0 的节点加入队列。
  3. 对于每个入度为 0 的节点,依次出队,更新其相邻节点的入度,将入度变为 0 的节点加入队列。
  4. 重复步骤 3 直到队列为空。

如果最终遍历过的节点数等于图中的节点数,说明图是有向无环图,可以得到一个拓扑排序。

01.课程表

题目链接:https://leetcode.cn/problems/course-schedule/

你这个学期必须选修 numCourses 门课程,记为 0numCourses - 1

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai必须 先学习课程 bi

  • 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

提示:

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= 5000
  • prerequisites[i].length == 2
  • 0 <= ai, bi < numCourses
  • prerequisites[i] 中的所有课程对 互不相同

思路

这里我们可以采用容器模拟邻接矩阵或者邻接表来进行拓扑排序,判断这个图是否有环的方式来解决这个问题

代码

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        unordered_map<int,vector<int>> edges;
        vector<int> in(numCourses,0);

        for(vector<int>& e:prerequisites){
            int a=e[0],b=e[1];
            edges[b].push_back(a);
            in[a]++;
        }

        queue<int> q;
        for(int i=0;i<numCourses;++i) 
            if(in[i]==0) q.push(i);

        while(!q.empty()){
            int t=q.front();
            q.pop();
            for(int e:edges[t]){
                in[e]--;
                if(in[e]==0) q.push(e);
            }
        }

        for(int i:in) if(i) return false;
        return true;
    }
};
  1. 使用一个哈希表 edges 存储有向图的边,其中 edges[b] 表示节点 b 指向的所有节点。
  2. 使用数组 in 记录每个节点的入度。初始化时,将所有节点的入度设为 0。
  3. 遍历先修课程的关系,构建有向图并更新入度数组。
  4. 将入度为 0 的节点加入队列 q
  5. 使用 BFS 进行拓扑排序,从入度为 0 的节点开始,依次出队,并将其邻接节点的入度减 1。如果邻接节点的入度减为 0,将其加入队列。
  6. 如果最终所有节点的入度都为 0,说明图中不存在环,可以完成所有课程,返回 true;否则,返回 false

02.课程表 II

题目链接:https://leetcode.cn/problems/course-schedule-ii/

现在你总共有 numCourses 门课需要选,记为 0numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai必须 先选修 bi

  • 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1]

返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:[0,1]
解释:总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。

示例 2:

输入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
输出:[0,2,1,3]
解释:总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。

示例 3:

输入:numCourses = 1, prerequisites = []
输出:[0]

思路

总体思路和上面一致,我们只需要在每次将入度为0的点顺序保存即为拓扑排序的顺序。

代码

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        unordered_map<int,vector<int>> edges;
        vector<int> in(numCourses,0);

        for(vector<int>& e:prerequisites){
            int a=e[0],b=e[1];
            edges[b].push_back(a);
            in[a]++;
        }

        queue<int> q;
        vector<int> ret;
        for(int i=0;i<numCourses;++i) 
            if(in[i]==0){
                q.push(i);
                ret.push_back(i);
            } 

        while(!q.empty()){
            int t=q.front();
            q.pop();
            for(int e:edges[t]){
                in[e]--;
                if(in[e]==0){ 
                    q.push(e);
                    ret.push_back(e);    
                }
            }
        }

        for(int i:in) if(i) return {};
        return ret;
    }
};
  1. 使用一个哈希表 edges 存储有向图的边,其中 edges[b] 表示节点 b 指向的所有节点。
  2. 使用数组 in 记录每个节点的入度。初始化时,将所有节点的入度设为 0。
  3. 遍历先修课程的关系,构建有向图并更新入度数组。
  4. 将入度为 0 的节点加入队列 q,同时将这些节点加入结果数组 ret 中。
  5. 使用 BFS 进行拓扑排序,从入度为 0 的节点开始,依次出队,并将其邻接节点的入度减 1。如果邻接节点的入度减为 0,将其加入队列和结果数组。
  6. 如果最终所有节点的入度都为 0,说明图中不存在环,返回拓扑排序结果;否则,返回空数组表示无法完成拓扑排序

03.火星词典

题目链接:https://leetcode.cn/problems/Jf1JuT

现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不同。

给定一个字符串列表 words ,作为这门语言的词典,words 中的字符串已经 按这门新语言的字母顺序进行了排序

请你根据该词典还原出此语言中已知的字母顺序,并 按字母递增顺序 排列。若不存在合法字母顺序,返回 "" 。若存在多种可能的合法字母顺序,返回其中 任意一种 顺序即可。

字符串 s 字典顺序小于 字符串 t 有两种情况:

  • 在第一个不同字母处,如果 s 中的字母在这门外星语言的字母顺序中位于 t 中字母之前,那么 s 的字典顺序小于 t
  • 如果前面 min(s.length, t.length) 字母都相同,那么 s.length < t.length 时,s 的字典顺序也小于 t

示例 1:

输入:words = ["wrt","wrf","er","ett","rftt"]
输出:"wertf"

示例 2:

输入:words = ["z","x"]
输出:"zx"

示例 3:

输入:words = ["z","x","z"]
输出:""
解释:不存在合法字母顺序,因此返回 "" 。

提示:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 100
  • words[i] 仅由小写英文字母组成

思路

将题意搞清楚之后,这道题就变成了判断有向图时候有环,可以用拓扑排序解决。
如何搜集信息(如何建图):
a. 两层for循环枚举出所有的两个字符串的组合;
b. 然后利用指针,根据字典序规则找出信息。

  1. 使用哈希表 edges 存储字母之间的顺序关系,其中 edges[a] 表示字母 a 后面可以跟随的字母集合。
  2. 使用哈希表 in 记录每个字母的入度,即有多少字母在它之前。
  3. 使用布尔变量 cheak 标记是否出现了无效的字母顺序。
  4. 定义 add 函数,该函数比较两个单词 s1s2,找到它们第一个不相同的字母,然后将这个字母的顺序关系添加到 edges 中。如果 s2s1 的前缀,则将 cheak 设置为 true
  5. 在构建字母之间的顺序关系时,遍历相邻的两个单词,调用 add 函数,如果 cheaktrue,则直接返回空字符串。
  6. 使用队列 q 存储入度为 0 的字母,初始化队列时将所有入度为 0 的字母加入。
  7. 使用 BFS 进行拓扑排序,不断将入度为 0 的字母出队,并将其后面可以跟随的字母的入度减 1。将入度为 0 的字母加入结果字符串 ret 中。
  8. 最后检查所有字母的入度是否都为 0,如果不为 0,则说明有环,返回空字符串;否则,返回结果字符串 ret

代码

class Solution {
    unordered_map<char,unordered_set<char>> edges;
    unordered_map<char,int> in;
    bool cheak=false;

    void add(string& s1,string& s2){
        int n=min(s1.size(),s2.size());
        int i=0;

        while(i<n){
            if(s1[i]!=s2[i]){
                char a=s1[i],b=s2[i];
                if(!edges.count(a)||!edges[a].count(b)){
                    edges[a].insert(b);
                    in[b]++;
                }
                break;
            }
            i++;
        }
        if(i==s2.size()&&i<s1.size()) cheak=true;
    }
public:
    string alienOrder(vector<string>& words) {
        for(auto& s:words)
            for(auto& ch:s)
                in[ch]=0;

        int n=words.size();
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++){
                add(words[i],words[j]);
                if(cheak) return "";
            }
        
        queue<char> q;
        for(auto& [a,b]:in)
            if(b==0) q.push(a);
        
        string ret;
        while(!q.empty()){
            char t=q.front();
            q.pop();
            ret+=t;
            for(char ch:edges[t])
                if(--in[ch]==0) q.push(ch);
        }

        for(auto& [a,b]:in) if(b) return "";

        return ret;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络防火墙综合实验

备注&#xff1a;电信网段15.1.1.0 移动网段14.1.1.0 办公区 11.1.1.0 生产区 10.1.1.0 服务区 13.1.1.0 公网 1.1.1.1 和 2.2.2.2 需求&#xff1a; 1、办公区设备可以通过电信链路和移动链路上网&#xff08;多对多nat&#xff0c;并且需要保留一个公网ip&#xff09; 2、…

第二证券:资金持续入场掘金成长板块

上证指数重回2900点、ETF成交活跃、科技股强势反弹……龙年首个交易日&#xff0c;在心情回暖、资金涌入的背景下&#xff0c;A股迎来开门红&#xff0c;组织也摩拳擦掌进行布局。 从仓位状况来看&#xff0c;私募排排网数据显现&#xff0c;到2月2日&#xff0c;私募全体仓位…

CPU设计之分支预测

1、引言 在之前的学习过程中&#xff0c;我们一直在假设程序是顺序执行的。PC每一次都默认4&#xff0c;一直这样周而复始。然而CPU就和人生一样&#xff0c;不可能一直一帆风顺的向前走。在某些场合我们总是需要做出决策&#xff0c;这些决策点&#xff0c;就像高速公路的一个…

litemall系统默认弱口令漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

Emlog博客网站快速搭建并结合内网穿透实现远程访问本地站点

文章目录 前言1. 网站搭建1.1 Emolog网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总结 前言 博客作为使…

VMware还原Windows11 ghost镜像

文章目录 环境步骤准备制作启动iso文件创建虚拟机启动虚拟机还原Windows 参考 环境 Windows 11 家庭中文版VMware Workstation 17 Pro石大师装机大师Windows 11 ghost系统镜像 步骤 准备 下载好Windows 11 ghost系统镜像&#xff0c;我下载的文件是 FQ_WIN11_X64_VDL_V2080…

list链表

1. list基本概念 功能&#xff1a;将数据进行链式存储 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成&#xff1a;链表由一系列结点组成 结点的组成&#xff1a;一个是存储数据…

Go 是否有三元运算符?Rust 和 Python 是怎么做的?

嗨&#xff0c;大家好&#xff01;本文是系列文章 Go 技巧第十四篇&#xff0c;系列文章查看&#xff1a;Go 语言技巧。 今天来聊聊在 Go 语言中是否支持三元运算符。这个问题很简单&#xff0c;没有。 首先&#xff0c;什么是三元运算符&#xff1f; 在其他一些编程语言中&a…

深入理解flinksql执行流程,calcite与catalog相关概念,扩展解析器实现语法的扩展

深入理解Flink Sql执行流程 1 Flink SQL 解析引擎1.1SQL解析器1.2Calcite处理流程1.2.1 SQL 解析阶段&#xff08;SQL–>SqlNode&#xff09;1.2.2 SqlNode 验证&#xff08;SqlNode–>SqlNode&#xff09;1.2.3 语义分析&#xff08;SqlNode–>RelNode/RexNode&#…

代码随想录算法训练营DAY20 | 二叉树(7) (续)

一、LeetCode 236 二叉树的最近公共祖先 题目链接&#xff1a;236.二叉树的最近公共祖先https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/ 思路&#xff1a;利用后序遍历是天然回溯过程、方便实现自底向上查找的原理&#xff0c;递归寻找公…

面试系列之《Spark》(持续更新...)

1.job&stage&task如何划分&#xff1f; job&#xff1a;应用程序中每遇到一个action算子就会划分为一个job。 stage&#xff1a;一个job任务中从后往前划分&#xff0c;分区间每产生了shuffle也就是宽依赖则划分为一个stage&#xff0c;stage这体现了spark的pipeline思…

普中51单片机学习(十四)

中断系统 中断的概念 CPU在处理某一事件A时&#xff0c;发生了另一事件B请求CPU迅速去处理&#xff08;中断发生&#xff09;,CPU暂时中断当前的工作&#xff0c;转去处理事件B&#xff08;中断响应和中断服务)&#xff0c;待CPU将事件B处理完毕后&#xff0c;再回到原来事件…

js_三种方法实现深拷贝

深拷贝&#xff08; 递归 &#xff09; 适用于需要完全独立于原始对象的场景&#xff0c;特别是当对象内部有引用类型时&#xff0c;为了避免修改拷贝后的对象影响到原始对象&#xff0c;就需要使用深拷贝。 // 原始对象 const obj { uname: Lily,age: 19,hobby: [乒乓球, 篮球…

BGP 邻居建立

拓扑图 配置 BGP进程号及为AS号 使用环回口建立BGP邻居关系时&#xff0c;需要指定更新源地址 EBGP在使用环回口建立邻居关系时&#xff0c;需配置EBGP多跳&#xff0c;环回口路由可达 EBGP的路由器存在IBGP邻居时&#xff0c;需要配置next-hop-local&#xff0c;保证下一跳…

Midjourney风格一致功能解读及使用方法

Midjourneys再次迎来更新&#xff0c;本次新增“风格一致”功能&#xff01;用户期待已久的风格模仿功能终于实现了&#xff01; --sref 虽然目前只是测试功能&#xff0c;但已经相当强大了&#xff0c;这篇文章我将带大家先睹为快&#xff01; 别忘了&#xff0c;这个功能目前…

Python中HTTP客户端库的比较与选择

在Python中&#xff0c;众多HTTP客户端库为我们提供了与Web服务进行交互的便利。每个库都有其独特的特点和适用场景&#xff0c;选择哪一个往往取决于项目的具体需求和个人偏好。下面&#xff0c;我们将对几个流行的HTTP客户端库进行比较&#xff0c;以助您在项目中做出明智的选…

Rabbitmq入门与应用(三)-RabbitMQ开发流程

RabbitMQ开发流程 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency>配置MQ 最简配置 spring:rabbitmq:host: mq的安装机器ipport: 5672username: ad…

如何查看 CPU 占用高的进程

1、使用 top 命令&#xff0c;查看 cpu 占用超过 100% 2、查看哪个进程占用 cpu 最高&#xff08;该案例使用阿里的 arthas 来查看&#xff09; 2.1 下载&#xff1a;curl -O https://arthas.aliyun.com/arthas-boot.jar 2.2 启动命令&#xff1a;java -jar arthas-boot.jar …

VPX信号处理卡设计原理图:9-基于DSP TMS320C6678+FPGA XC7V690T的6U VPX信号处理卡 信号处理 无线电通信

一、概述 本板卡基于标准6U VPX 架构&#xff0c;为通用高性能信号处理平台&#xff0c;系我公司自主研发。板卡采用一片TI DSP TMS320C6678和一片Xilinx公司Virtex 7系列的FPGA XC7V690T-2FFG1761I作为主处理器&#xff0c;Xilinx 的Aritex XC7A200T作为辅助处理器。XC7A2…