代码训练LeetCode(2)区间列表的交集

代码训练(2)LeetCode之区间列表的交集

Author: Once Day Date: 2024年3月5日

漫漫长路,才刚刚开始…

全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客

参考文章:

  • 986. 区间列表的交集 - 力扣(LeetCode)
  • 力扣 (LeetCode) 全球极客挚爱的技术成长平台

文章目录

      • 代码训练(2)LeetCode之区间列表的交集
        • 1. 问题
        • 2. 分析
        • 3. 代码实现
          • 3.1 代码具体含义解释
          • 3.2 运行结果如下所示
        • 4. 结论

1. 问题

给定两个由一些 闭区间 组成的列表,firstListsecondList ,其中 firstList[i] = [starti, endi]secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序

返回这 两个区间列表的交集

形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b

两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3][2, 4] 的交集为 [2, 3]

比如对于以下两个区间:

firstList = [[1, 8], [9, 12]]
secondList = [[0, 3], [4, 5], [6, 10]]

其区间如下所示:

在这里插入图片描述

从图上可以看出,有交集的区间是[[1, 3], [4, 5], [6, 8], [9, 10]]

2. 分析

该问题是一个有点难度的编程题目,我们用C语言来实现,并且额外要求性能较高(速度90%,内存80%)。

想象你手中有两根彩色的绳子,这两根绳子由不同颜色的段落组成,每个颜色的段落代表一个闭区间。现在你的任务是找出这两根绳子中颜色相叠的部分,这些部分就是两组区间的交集。

(1) 原题解析

  • 两个列表都是由闭区间组成,已经根据区间的起点进行了排序。
  • 两个列表中的区间都是不相交的,也就是说列表内部不会有重叠的部分。
  • 我们需要返回的是两个列表中相互重叠区间的集合。

(2) 解题思路

  1. 使用两个指针分别遍历两个列表,初始时都指向各自列表的第一个区间。
  2. 对于每一对区间,比较它们的起点和终点来确定是否有交集。
  3. 如果有交集,计算出交集区间,并将其加入到结果集中。
  4. 移动指针来继续检测下一对可能重叠的区间,直到至少有一个列表被完全遍历。

(3) 分析步骤

  • 判断两个区间是否有交集的条件是,一个区间的起点小于或等于另一个区间的终点。
  • 交集区间的起点是两个区间起点的最大值,终点是两个区间终点的最小值。
  • 如果firstList的区间终点小于secondList的区间终点,移动firstList的指针;反之,则移动secondList的指针。

(4) 性能优化关键点

  • 执行速度:由于两个列表都已排序,我们可以利用这一点来减少不必要的比较,从而提高执行速度。
  • 内存使用:可以在原地记录结果,避免使用额外的内存空间。
3. 代码实现

假设firstList = [[1,3],[5,9]]secondList = [[2,4],[8,10]]

  1. 比较[1,3][2,4],有交集[2,3]
  2. 比较[5,9][2,4],没有交集,[2,4]的终点小,移动secondList指针。
  3. 比较[5,9][8,10],有交集[8,9]
  4. 结果集为[[2,3],[8,9]]

下面是C语言的实现代码:

int** intervalIntersection(int** firstList, int firstListSize, int* firstListColSize, int** secondList, int secondListSize, int* secondListColSize, int* returnSize, int** returnColumnSizes) {int **result = malloc(sizeof(int *) * (firstListSize + secondListSize));*returnColumnSizes = malloc(sizeof(int) * (firstListSize + secondListSize));int i = 0, j = 0, k = 0;#define Max(a, b) (a > b ? a : b)
#define Min(a, b) (a < b ? a : b)while (i < firstListSize && j < secondListSize) {int startMax = Max(firstList[i][0], secondList[j][0]);int endMin = Min(firstList[i][1], secondList[j][1]);if (startMax <= endMin) { // There is an intersectionresult[k] = malloc(sizeof(int) * 2);result[k][0] = startMax;result[k][1] =endMin;(*returnColumnSizes)[k++] = 2;}if (firstList[i][1] < secondList[j][1]) {i++;} else {j++;}}*returnSize = k;return result;
}
3.1 代码具体含义解释

上面代码可以计算两个列表 firstListsecondList 中表示的区间集合的交集。每个列表中的每个区间都由一对整数表示,这对整数分别标示了区间的开始和结束。列表中的区间已经以升序排列,并且在单个列表内,区间不会重叠和相连。

函数的参数解释如下:

  • int** firstList:指向第一个区间列表的指针。
  • int firstListSize:第一个列表中区间的数量。
  • int* firstListColSize:指向一个数组,其中每个元素表示 firstList 中对应区间的大小(在这个场景中,每个区间由两个整数组成,所以该数组的每个元素应该都是 2)。
  • int** secondList:指向第二个区间列表的指针。
  • int secondListSize:第二个列表中区间的数量。
  • int* secondListColSize:指向一个数组,其中每个元素表示 secondList 中对应区间的大小(同样,每个元素应该都是 2)。
  • int* returnSize:指向一个整数的指针,该整数表示交集列表中区间的数量。
  • int** returnColumnSizes:指向一个指针的指针,用来分配和返回交集列表中每个区间的大小。

函数的主要逻辑如下:

  1. 动态分配内存空间给 result,这是用来存储区间交集结果的指针数组。大小为两个列表大小之和,这是因为最坏的情况下,交集的数量可能等于两个列表中区间数量之和。
  2. 动态分配内存空间给 *returnColumnSizes,用来存储每个交集区间的大小(在本函数中,每个交集区间大小都是 2)。
  3. 使用两个索引 ij 分别遍历 firstListsecondListk 用来记录 result 中交集区间的数量。
  4. 使用宏 MaxMin 来计算两个区间的最大开始点和最小结束点,以确定它们是否有交集。
  5. 如果两个区间有交集(startMax <= endMin),则将这个交集区间存储在 result[k] 中,并且将 (*returnColumnSizes)[k] 设置为 2,然后递增 k
  6. 然后,比较两个区间的结束点,将结束较早的区间的索引(ij)递增,以移动到下一个区间。
  7. 重复步骤 4 到 6,直到任一列表全部遍历完。
  8. 设置 *returnSizek,即交集区间的实际数量。
  9. 返回 result,它指向包含所有交集区间的指针数组。

函数结束后,调用者将得到一个包含所有交集区间的数组 result,以及两个输出值:*returnSize 表示 result 中区间的数量,*returnColumnSizes 表示 result 中每个区间的大小。调用者需要负责释放 result 数组和 *returnColumnSizes 数组的堆内存。

3.2 运行结果如下所示

在这里插入图片描述

从运行速率来看,离预定目标还差一些,我们来简单分析一下热点代码

除了一开始分配了大量内存之外,后续每次找到交集,都需要分配新内存,因此有大量的malloc操作,在C语言里,分配内存是相对耗时较久的任务。

该问题获取交集时,对于两个比较的集合元素,总是取其中一个元素用于下一轮比较,那么此时。另外一个元素就可以用来存储可能得闭区间。此外优化数组访问和比较流程,减少指令数,如下:

int** intervalIntersection(int** firstList, int firstListSize, int* firstListColSize, int** secondList, int secondListSize, int* secondListColSize, int* returnSize, int** returnColumnSizes) {int **result = malloc(sizeof(int *) * (firstListSize + secondListSize));if (firstListSize ==0 || secondListSize == 0) {*returnSize=0;return result;}*returnColumnSizes = malloc(sizeof(int) * (firstListSize + secondListSize));int i = 0, j = 0, k = 0;while (i < firstListSize && j < secondListSize) {int *first = firstList[i];int *second = secondList[j];if (first[0] <= second[1] && second[0] <= first[1]) {if (first[1] <= second[1]) {if (second[0] > first[0]) {first[0] = second[0];}i++;result[k++] = first;} else {if (second[0] < first[0]) {second[0] = first[0];}j++;result[k++] = second;}} else {if (first[0] < second[0]) {i++;} else {j++;}}}int *temp = *returnColumnSizes;for (i = 0; i < k; i++) {temp[i] = 2;	}*returnSize = k;return result;
}

函数的参数解释如下:

  • int** firstList: 指向第一个区间列表的指针。
  • int firstListSize: 第一个列表中区间的数量。
  • int* firstListColSize: 指向数组的指针,其中包含firstList中每个区间的大小。
  • int** secondList: 指向第二个区间列表的指针。
  • int secondListSize: 第二个列表中区间的数量。
  • int* secondListColSize: 指向数组的指针,其中包含secondList中每个区间的大小。
  • int* returnSize: 指向结果的区间数量的指针。
  • int** returnColumnSizes: 指向数组的指针,该数组将包含结果中每个区间的大小。

函数的主要步骤如下:

  1. 分配内存以存储结果的区间列表。
  2. 如果两个列表中任何一个的大小为0,立即返回空的结果。
  3. 分配内存来存储结果列表中每个区间的大小。
  4. 使用两个指针ij遍历两个列表,寻找交集。
  5. 对每一对潜在的交集区间,检查它们是否相交,如果相交,计算交集并将其添加到结果列表中。
  6. 如果两个区间有交集,那么将交集区间添加到结果列表中,并递增对应的列表指针。
  7. 如果两个区间没有交集,递增指向较早开始区间的列表指针。
  8. 遍历结果列表,为每个区间设置大小为2(因为区间由一对整数表示)。
  9. 设置返回的区间数量。
  10. 返回结果列表。

有一点需要注意的是,这个函数在找到交集时并没有创建新的区间,而是直接使用了原列表中的指针。这意味着结果列表中的区间指针指向了firstListsecondList中的原始区间,可能在必要时修改了原始区间的起始点以反映交集的起始点。

这段代码重点在于如下几点

  1. 在一开始通过判断将特殊情况排除,可以提高部分场景的性能。
  2. 原地修改数据节点,不申请内存,因此效率较高,且内存使用较少。
  3. 优化判断逻辑,将主要判断提到开始,减少不必要判断。

下面是运行测试数据,成功达到预期性能目标:

在这里插入图片描述

4. 结论

这个问题就像是找出两根彩色绳子中相同颜色覆盖的部分。我们通过设置两个指针,分别在两个列表上移动,寻找可能重叠的区间。计算可能的交集,并且根据区间终点的大小决定移动哪个指针。这种方法因为没有使用额外的数据结构,所以在内存使用上非常经济,同时由于两个列表的有序性,使得我们能够以线性的时间复杂度完成遍历,保证了算法的执行效率。通过这种方式,我们可以得到一个包含所有交集区间的列表,这正是题目所要求的结果。







Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flutterprovider局部刷新,简单聊聊2024年Android开发的现状和思考

一、java面试题 熟练掌握java是很关键的&#xff0c;大公司不仅仅要求你会使用几个api&#xff0c;更多的是要你熟悉源码实现原理&#xff0c;甚至要你知道有哪些不足&#xff0c;怎么改进&#xff0c;还有一些java有关的一些算法&#xff0c;设计模式等等。 &#xff08;一&…

使用GitOps自动化推动AI/ML工作流程

作为一名深耕自动化和人工智能领域的开发人员&#xff0c;我们逐渐认识到尖端工具和方法之间的显着协同作用&#xff0c;这些协同作用突破了可能性的界限。在这次探索中&#xff0c;我们想分享一个概念&#xff0c;它不仅彻底改变了我们的软件开发和基础设施管理方法&#xff0…

微信小程序开发系列(十七)·事件传参·mark-自定义数据

目录 步骤一&#xff1a;按钮的创建 步骤二&#xff1a;按钮属性配置 步骤三&#xff1a;添加点击事件 步骤四&#xff1a;参数传递 步骤五&#xff1a;打印数据 步骤六&#xff1a;获取数据 步骤七&#xff1a;父进程验证 总结&#xff1a;data-*自定义数据和mark-自定…

绘图设计:用Draw.io绘制图形技巧大全(含统一建模语言UML模板)

一、常见UML模板 1.流程图 2.用例图 include是包含关系&#xff0c;extend是扩展关系 简而言之&#xff0c;include是子集指向父集&#xff1b;而extend是扩展用例指向基础用例&#xff08;基础用例可以理解为系统核心功能&#xff0c;扩展用例是可选的&#xff0c;不是必须…

易基因:NAR:RCMS编辑系统在特定细胞RNA位点的靶向m5C甲基化和去甲基化研究|项目文章

喜讯&#xff01;易基因表观转录组学RNA-BS技术服务见刊《核酸研究》 大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 2024年2月15日&#xff0c;吉林大学张涛、赵飞宇、李金泽为共同第一作者&#xff0c;吉林大学李占军、隋婷婷及赖良…

大数据技术学习笔记(五)—— MapReduce(2)

目录 1 MapReduce 的数据流1.1 数据流走向1.2 InputFormat 数据输入1.2.1 FileInputFormat 切片源码、机制1.2.2 TextInputFormat 读数据源码、机制1.2.3 CombineTextInputFormat 切片机制 1.3 OutputFormat 数据输出1.3.1 OutputFormat 实现类1.3.2 自定义 OutputFormat 2 Map…

安卓类加载机制

目录 一、ClassLoader介绍二、双亲委托机制三、类的加载过程 一、ClassLoader介绍 任何一个 Java 程序都是由一个或多个 class 文件组成&#xff0c;在程序运行时&#xff0c;需要将 class 文件加载到 JVM 中才可以使用&#xff0c;负责加载这些 class 文件的就是 Java 的类加…

使用API有效率地管理Dynadot域名,进行DNS域名解析

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…

linux 将 api_key设置环境变量里

vi ~/.bashrc在最后添加api_key的环境变量 export GEMINI_API_KEYAIza**********WvpX7FwbdM刷新配置 source ~/.bashrc使用python 读取环境变量 import os gemini_api_key os.getenv(GEMINI_API_KEY) print(gemini_api_key)

【DevOps云实践】不同Azure Function的类型

【DevOps云实践】不同Azure Function的类型 Azure函数是由Microsoft Azure提供的无服务器计算服务,允许开发人员构建和部署应用程序而不必担心底层基础设施。使用Azure函数,您可以根据不同的触发器执行代码,并支持多种类型的函数以满足不同的用例。在本博客文章中,我们将探…

springboot + jpa + 达梦数据库兼容 Mysql的GenerationType.IDENTITY主键生成策略

导入达梦数据库对hibernate的方言包 <dependency><groupId>com.dameng</groupId><artifactId>DmDialect-for-hibernate5.6</artifactId><version>8.1.2.192</version></dependency>配置文件中添加方言配置和主键生成策略配置…

VBA自适应多种排班计划日期填充

实例需求&#xff1a;某公司有两种不同排班计划 MWF: 周一周三周五-周一周三周五…TTS: 周二周四周六-周二周四周六… 但是数据表中有时会缺少部分日期&#xff0c;为了便于汇总多个部分的数据&#xff0c;现在需要将日期补全&#xff0c;对于补充的日期标记为黄色。 先讨论一…

第一个 Angular 项目 - 添加路由

第一个 Angular 项目 - 添加路由 前置项目是 第一个 Angular 项目 - 添加服务&#xff0c;之前的切换页面使用的是 ngIf 对渲染的组件进行判断&#xff0c;从而完成渲染。这一步的打算是添加路由&#xff0c;同时添加 edit recipe 的功能(同样通过路由实现) 用到的内容为&…

解决物理机装不上VMnet1和VMnet8的虚拟网卡问题

问题描述&#xff1a; 博主在使用虚拟机时&#xff0c;发现物理机的ping命令连接不上虚拟机&#xff0c;导致xshell软件也连接不上&#xff0c;最后发现问题是更改适配器设置中没有虚拟机的网卡&#xff08;VMnet1和VMnet8&#xff09;&#xff1a; 方法一&#xff1a; 博主搜…

【MySQL】深入解析日志系统:undo log、redo log、bin log

文章目录 前言1、undo log1.1、undo log 是什么1.2、事务回滚 2、redo log2.1、redo log 是什么2.2、redo log 刷盘2.3、redo log 硬盘文件 3、bin log3.1、bin log 是什么3.2、bin log 和 redo log 区别3.3、bin log 刷盘3.4、两阶段提交 前言 MySQL数据库提供了功能强大的日…

LeetCode 1976.到达目的地的方案数:单源最短路的Dijkstra算法

【LetMeFly】1976.到达目的地的方案数&#xff1a;单源最短路的Dijkstra算法 力扣题目链接&#xff1a;https://leetcode.cn/problems/number-of-ways-to-arrive-at-destination/ 你在一个城市里&#xff0c;城市由 n 个路口组成&#xff0c;路口编号为 0 到 n - 1 &#xff…

使用vite创建一个vue3项目

创建一个vue3项目 1.使用命令npm create vuelatest来创建一个vue3项目&#xff0c;注意&#xff1a;官网说明了必须node版本是18及以上的&#xff0c;这边需要注意下 2.然后根据提示进入项目目录 先npm install安装依赖&#xff0c;然后npm run dev启动项目 大家可以看到&am…

Windows安装Go语言及VScode配置

最近搞自己的网站时突然想起来很多上学时的事&#xff0c;那会美国总统还是奥巴马&#xff0c;网页课教的是DreamWeaver跟Photoshop&#xff0c;其他语言像PHP、Java8、Python都有学一点&#xff0c;讲究一个所见即所得。虽然是信管专业那时和斌桑班长对新语言很感兴趣&#xf…

分享一个完全免费的GPT4站点,gpts也可以用

给大家分享一个完全免费的GPT4站点&#xff0c;gpts也可以用点击链接可用

【Leetcode】1588.所有奇数长度子数组的和

题目描述 思路 题目要求我们求解所有奇数长度数组的和。若暴力循环求解&#xff0c;时间复杂度过高。所以&#xff0c;我们可以采用前缀和优化。 如上图输入arr数组&#xff0c;sum[i]用于计算arr数组中前i个数的和。(在程序中&#xff0c;先给sum[0]赋值&#xff0c;等于arr[0…