剑指Offer题目笔记32(拓扑排序)

面试题113:

面试题113

解决方案:
  1. 将课程看成图中的节点,如果两门课程存在先修顺序那么它们在图中对应的节点之间存在一条从先修课程到后修课程的边,因此这是一个有向图。
  2. 可行的修课序列实际上是图的拓扑排序序列。图中的每条边都是从先修课程指向后修课程,而拓扑排序能够保证任意一条边的起始节点一定排在终止节点的前面,因此拓扑排序得到的序列与先修顺序一定不会存在冲突,于是这个问题转变成如何求有向图的拓扑排序序列。对有向图进行拓扑排序的算法是每次找出一个入度为0的节点添加到序列中,然后删除该节点及所有以该节点为起点的边。重复这个过程,直到图为空或图中不存在入度为0的节点。
源代码:
import java.util.*;

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        Map<Integer, List<Integer>> graph = new HashMap<>();
        
        // 初始化图
        for (int i = 0; i < numCourses; i++) {
            graph.put(i, new LinkedList<Integer>());
        }

        int[] inDegrees = new int[numCourses];
        
        // 构建图和入度数组
        for (int[] prereq : prerequisites) {
            int course = prereq[0];
            int prerequisite = prereq[1];
            graph.get(prerequisite).add(course);
            inDegrees[course]++;
        }

        Queue<Integer> queue = new LinkedList<>();
        
        // 将入度为 0 的节点加入队列
        for (int i = 0; i < numCourses; i++) {
            if (inDegrees[i] == 0) {
                queue.add(i);
            }
        }

        List<Integer> order = new LinkedList<>();
        
        // 拓扑排序
        while (!queue.isEmpty()) {
            int course = queue.remove();
            order.add(course);
            for (int next : graph.get(course)) {
                inDegrees[next]--;
                if (inDegrees[next] == 0) {
                    queue.add(next);
                }
            }
        }

        // 如果能够修完所有课程,则返回学习顺序;否则返回空数组
        return order.size() == numCourses ? order.stream().mapToInt(i -> i).toArray() : new int[0];
    }
}

面试题114:

面试题114

解决方案:
  1. 在排序的单词列表[“ac”,“ab”,“bc”,“zc”,“zb”]中,一共出现了4个字母,即’a’、‘b’、‘c’和’z’。需要根据单词的顺序确定这个4个字母的顺序。由于"ac"排在"ab"的前面,因此字母’c’应该排在字母’b’的前面(即’c’<’b’)。这是因为这两个单词的第1个字母相同,第2个字母不同,那么它们的第2个字母的顺序确定了两个单词的顺序。接下来两个相邻的单词是"ab"和"bc",它们的第1个字母就不同,那么它们的顺序由它们的第1个字母确定,所以’a’<’b’。类似地,可以根据"bc"排在"zc"的前面得知’b’<’z’,根据"zc"排在"zb"的前面得知’c’<’b’。
  2. 由比较排序的单词列表中两两相邻的单词可知’c’<’b’、‘a’<’b’和’b’<’z’,现在需要找出一个包含4个字母的字母序列满足已知的3个字母的大小顺序。这看起来就是一个关于拓扑排序的问题,可以将每个字母看成图中的一个节点。如果已知两个字母的大小关系,那么图中就有一条从较小的字母指向较大的字母的边。
源代码:
class Solution {
    public String alienOrder(String[] words) {
        Map<Character, Set<Character>> graph = new HashMap<>();
        //inDegrees保存每个节点的入度
        Map<Character, Integer> inDegrees = new HashMap<>();
        for (String word : words) {
            for (char ch : word.toCharArray()) {
                graph.putIfAbsent(ch, new HashSet<>());
                inDegrees.put(ch, 0);
            }
        }
		
        for (int i = 1; i < words.length; i++) {
            String w1 = words[i - 1];
            String w2 = words[i];
            if (w1.startsWith(w2) && !w1.equals(w2)) {
                return "";
            }
			//从头找出第1组不同的两个字母,在图中添加一条从较小的字母(ch1)指向较大的字母(ch2)的边。
            for (int j = 0; j < w1.length() && j < w2.length(); j++) {
                char ch1 = w1.charAt(j);
                char ch2 = w2.charAt(j);
                if (ch1 != ch2) {
                    if (!graph.get(ch1).contains(ch2)) {
                        graph.get(ch1).add(ch2);
                        inDegrees.put(ch2, inDegrees.get(ch2) + 1);
                    }

                    break;
                }
            }
        }
		
        Queue<Character> queue = new LinkedList<>();
        for (char ch : inDegrees.keySet()) {
            //将入度为0的字符添加到队列中
            if (inDegrees.get(ch) == 0) {
                queue.add(ch);
            }
        }

        StringBuilder sb = new StringBuilder();
        while (!queue.isEmpty()) {
            char ch = queue.remove();
            sb.append(ch);
            //将以ch为起点的边删除,那么ch指向的节点的入度减一
            for (char next : graph.get(ch)) {
                inDegrees.put(next, inDegrees.get(next) - 1);
                if (inDegrees.get(next) == 0) {
                    queue.add(next);
                }
            }
        }

        return sb.length() == graph.size() ? sb.toString() : "";
    }
}

面试题115:

面试题115

解决方案:
  1. 按照题目的要求,如果在seqs的某个序列中数字i出现在数字j的前面,那么由seqs重建的序列中数字i一定也要出现在数字j的前面。也就是说,重建序列的数字顺序由seqs的所有序列定义。可以将seqs中每个序列的每个数字看成图中的一个节点,两个相邻的数字之间有一条从前面数字指向后面数字的边。
  2. 如果得到的是有向图的拓扑排序序列,那么任意一条边的起始节点在拓扑排序序列中一定位于终止节点的前面。因此,由seqs重建的序列就是由seqs构建的有向图的拓扑排序的序列。这个问题就转变成判断一个有向图的拓扑排序序列是否唯一。
源代码:
class Solution {
    public boolean sequenceReconstruction(int[] nums, int[][] sequences) {
        Map<Integer, Set<Integer>> graph = new HashMap<>();
        Map<Integer, Integer> inDegrees = new HashMap<>();
        for (int[] sequence : sequences) {
            for (int num : sequence) {
                if (num < 1 || num > nums.length) {
                    return false;
                }

                graph.putIfAbsent(num, new HashSet<>());
                inDegrees.putIfAbsent(num, 0);
            }

            for (int i = 0; i < sequence.length - 1; i++) {
                int num1 = sequence[i];
                int num2 = sequence[i + 1];
                if (!graph.get(num1).contains(num2)) {
                    graph.get(num1).add(num2);
                    inDegrees.put(num2, inDegrees.get(num2) + 1);
                }
            }
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int num : inDegrees.keySet()) {
            if (inDegrees.get(num) == 0) {
                queue.add(num);
            }
        }

        List<Integer> built = new LinkedList<>();
        //入度为0的节点只能同时出现一个
        while (queue.size() == 1) {
            int num = queue.remove();
            built.add(num);
            for (int next : graph.get(num)) {
                inDegrees.put(next, inDegrees.get(next) - 1);
                if (inDegrees.get(next) == 0) {
                    queue.add(next);
                }
            }
        }

        int[] result = new int[built.size()];
        result = built.stream().mapToInt(i -> i).toArray();
        return Arrays.equals(result, nums);

    }
}

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

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

相关文章

C++并发编程

基本介绍 线程 C98标准没有直接提供原生的多线程支持 在C98中&#xff0c;并没有像后来的C11标准中那样的<thread>库或其他直接的多线程工具 然而&#xff0c;这并不意味着在C98中无法实现多线程。开发者通常会使用平台特定的API&#xff08;如Windows的线程API或POSI…

前端开发攻略---用原生JS在网页中也能实现 文本转语音!

1、原理 语音合成 (也被称作是文本转为语音&#xff0c;英语简写是 tts) 包括接收 app 中需要语音合成的文本&#xff0c;再在设备麦克风播放出来这两个过程。 Web API中对此有一个主要控制接口 SpeechSynthesis&#xff0c;外加一些处理如何表示要被合成的文本 (也被称为 utte…

玩转微服务-SonarQube

这里写目录标题 第一节 SonarQube1.1 简介1.2 四个组成部分1.2.1 SonarQube服务器1.2.2 SonarQube数据库1.2.3 插件1.2.4 Scanner 1.3 工作流程 第二节 SonarQube的安装2.1 安装2.2 插件 第三节 P3C规范3.1 简介3.2 SonarQube 配置 P3C规范3.3 IDEA配置 P3C规范 第四节 Maven项…

机器学习-期末复习

本文的内容按照作者的课程考试要求书写&#xff0c;仅供复习参考。&#x1f337;&#x1f337;&#x1f337;欢迎大家指正&#xff01; 机器学习是一种人工智能&#xff08;AI&#xff09;的分支领域&#xff0c;它致力于开发能够通过数据学习和改进的算法和模型。简而言之&…

2024年AIGC+教育行业报告

在宏观层面上&#xff0c;如果把人工智能看作一种生命体&#xff0c;AIGC教育的内涵其实是碳基生命和硅基生命的 交互和培育问题。AIGC技术是对人脑计算、思考、判断等内在能力的延伸&#xff0c;是人的智能在机器形态 上的规模化聚集、运作和反应。由此&#xff0c;部分基础性…

【漏洞复现】云时空社会化商业ERP系统LoginName SQL注入漏洞

漏洞描述&#xff1a; 云时空社会化商业ERP系统loginName存在SQL注入漏洞&#xff0c;攻击者可以通过此漏洞获取数据库敏感信息。 搜索语法: Fofa-Query: app"云时空社会化商业ERP系统" 漏洞详情&#xff1a; 1.云时空社会化商业ERP系统。 2.漏洞POC&#xff1a…

倒计时开始!Big Demo Day第十二期,揭秘DePIN,探索Web3未来(附参会指南)

香港—— 全球领先的 Web3.0 活动 Big Demo Day 第十二期即将于 4 月 26 日在香港数码港盛大举行。本次活动由知名科技企业 ZeeprLabs 赞助&#xff0c;Central Research 主办&#xff0c;并得到 Techub News 的联合主办以及数码港、852Web3 等机构的合作支持。 在过去的 11 期…

鸿蒙HarmonyOS应用 - ArkUI组件

ArkUI组件 基础组件 Image 声明Image组件并设置图片源 网络权限&#xff1a;ohos.permission.INTERNET Image(scr: string | PixelMap | Resource)// 1. string&#xff1a;用于加载网络图片&#xff0c;需要申请网络权限 Image("https://xxx.png")// 2. PixelMap…

驱鸟器低成本OTP语音方案选型-wtn6020唯创知音

一、开发背景&#xff1a; 随着农业现代化的不断推进&#xff0c;鸟类对农作物的侵扰问题愈发严重。传统的驱鸟方法&#xff0c;如人工驱赶或使用化学药剂&#xff0c;不仅效率低下&#xff0c;而且可能对环境造成污染。因此&#xff0c;开发一种高效、环保、低成本的驱鸟器成…

考研日常记录(upd 24.4.24)

由于实在太无聊了 &#xff0c; 所以记录以下考研备考日常 &#xff0c; 增加一点成就感 &#xff0c; 获得一点前进动力。 文章目录 2024.4.18 周四课程情况&#xff1a;时间规划&#xff1a; 2024.4.19 周五课程情况&#xff1a;时间规划&#xff1a; 2024.4.20 周六2024.4.2…

RK3588构建ubuntu22.04根文件系统

前言 RK系列的平台提供了buildroot和debian的系统&#xff0c;使用脚本可以直接构建出来&#xff0c;但是没有提供ubuntu的系统&#xff0c;很多厂商只提供一个rootfs.img的固件包&#xff0c;没有将方法开源出来。本文实现了从ubuntu官网开始构建一个ubuntu22.04根文件系统。…

SSTV音频转图片

SSTV工具有很多&#xff0c;这里使用RX-SSTV慢扫描工具 下载安装 RX-SSTV解码软件 下载地址&#xff1a;https://www.qsl.net/on6mu/rxsstv.htm 一直点下一步&#xff0c;安装成功如下图: 虚拟声卡e2eSoft 由于SSTV工具是根据音频传递图片信息&#xff0c;正常解法需要一…

人耳的七个效应

1、掩蔽效应 • 人们在安静环境中能够分辨出轻微的声音&#xff0c;即人耳对这个声音的听域很低&#xff0c;但在嘈杂的环境中轻微的声音就会被淹没掉&#xff0c;这时将轻微的声音增强才能听到。 • 这种在聆听时&#xff0c;一个声音的听阈因另一声音的出现而提高的现象&…

本地修改localhost--手把手

找到本地hosts文件 1、C:\Windows\System32\drivers–快捷键ctrlR,输入drivers 2、点击etc目录&#xff0c;找到hosts文件&#xff0c;右键使用记事本方式打开编辑 3、添加自己想得到的域名【只能在本地使用】 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.…

面试:JVM内存结构

一、Java代码的运行步骤 一段Java代码先会被反编译为Java字节码&#xff0c;当执行java命令时&#xff0c;JVM虚拟机会被创建出来&#xff0c;并会创建一个main主线程来执行主方法。 二、JVM的内存结构有哪些&#xff1f; 1、方法区&#xff1a;&#xff08;线程共享&#xff…

Linux交换空间的创建使用

交换空间&#xff1a; 换出&#xff1a;将内存中不常用&#xff08;冷数据&#xff09;的放去硬盘里 换出&#xff1a;内存要使用这部分数据时&#xff0c;将硬盘的这部分数据放入内存 在内存和硬盘上用来交换数据的空间就是交换空间 创建交换空间的步骤 1.去磁盘上创建一个分…

Linux中的高级IO函数(一)pipe socketpair dup

Linux提供了很多高级的I/O函数。它们并不像Linux基础I/O函数&#xff08;比如open和read&#xff09;那么常用&#xff08;编写内核模块时一般要实现这些I/O函数&#xff09;&#xff0c;但在特定的条件下却表现出优秀的性能。这些函数大致分为三类&#xff1a; 用于创建文件描…

Mongodb语法使用说明(含详细示例)

点击下载《Mongodb语法使用说明&#xff08;含详细示例&#xff09;》 1. 前言 MongoDB是一款高性能、开源、面向文档的NoSQL数据库&#xff0c;它使用类似JSON的BSON格式存储数据&#xff0c;提供了灵活的数据模型和强大的查询功能。本文将详细介绍MongoDB数据库的基本增删改…

CSS常用属性之(列表、表格、鼠标)属性,(如果想知道CSS的列表、表格、鼠标相关的属性知识点,那么只看这一篇就足够了!)

前言&#xff1a;在学习CSS的时候&#xff0c;必不可少的就要学习选择器和常见的属性&#xff0c;而本篇文章讲解的是CSS中的列表、表格、背景、鼠标属性。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 大致了解一下本篇文章…

new String和直接赋值的一些问题

分析1 我们先看以下代码&#xff1a; String str1 "abc"; // 在常量池中String str2 new String("abc"); // 在堆上System.out.println(str1 str2)以上结果的输出是什么&#xff1f; 输出&#xff1a;false 前置知识&#xff1a; 在JVM中&#xff0c…