代码训练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 intersection
            result[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

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

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

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

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

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

相关文章

推荐一款素材资源下载神器 —— 有图下载器

​ 最近由于学习工作需要&#xff0c;得从小红薯上搬运一些学习视频&#xff0c;一直找不到一个好用的视频搬运软件&#xff0c;朋友向我介绍推荐了一款工具叫作有图视频下载器&#xff0c;试了试感觉这款软件真的非常好用&#xff0c;所以推荐给大家&#xff01; 一、支持提…

定时执行专家的主要功能和使用场景

定时执行专家是一款功能强大且实用的定时任务软件。它具有以下优点&#xff1a; 功能丰富: 支持多种定时模式、多种任务类型、丰富的触发方式、强大的日志功能等。易于使用: 操作界面简洁直观&#xff0c;易于上手。稳定可靠: 运行稳定可靠&#xff0c;可长期使用。 具体来说&…

flutter框架优缺点,最新Android大厂高频面试题

部分面试常问的面试专题 一、Java篇 1.多线程并发&#xff1b; sleep 和 wait 区别join 的用法线程同步&#xff1a;synchronized 关键字等线程通信线程池手写死锁 2.Java 中的引用方式&#xff0c;及各自的使用场景 3.HashMap 的源码 4.GC(垃圾回收)是什么&#xff1f;如何…

uniapp 手写 简易 时间轴 组件

一、案例如图 该案例设计条件&#xff1a; 左侧时间 和竖线、点、内容都是居中对其的&#xff0c;上下时间点中间要有一段距离 二、编写逻辑 1. 布局结构&#xff1a;一共三个元素&#xff0c;左侧是时间和黑点&#xff0c;中间是线条&#xff0c;右侧是内容 2. 样式难点&#…

第五篇:人工智能与机器学习技术VS创意创新(creative)--- 我为什么要翻译介绍美国人工智能科技巨头IAB公司?

【如无特殊说明&#xff0c;本文所有图片均来源于网络】 IAB平台&#xff0c;使命和功能 IAB成立于1996年&#xff0c;总部位于纽约市。 作为美国的人工智能科技巨头社会媒体和营销专业平台公司&#xff0c;互动广告局&#xff08;IAB- the Interactive Advertising Bureau&…

(学习日记)2024.03.05:UCOSIII第七节:SysTick+任务时间片

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

java-ssm-jsp-大学社团管理系统

java-ssm-jsp-大学社团管理系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

pyg-创建消息传递网络

创建消息传递网络 — pytorch_geometric 文档 (pytorch-geometric.readthedocs.io) https://arxiv.org/abs/1801.07829 import torch from torch.nn import Sequential as Seq, Linear, ReLU from torch_geometric.nn import MessagePassing class EdgeConv(MessagePassing): …

Cloud-Sleuth分布式链路追踪(服务跟踪)

简介 在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的服务节点调用来协同产生最后的请求结果,每一个前端请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败 GitHub - spring-cloud/spring-cloud-sl…

Java基础 - 8 - 算法、正则表达式

一. 算法 什么是算法&#xff1f; 解决某个实际问题的过程和方法 学习算法的技巧&#xff1f; 先搞清楚算法的流程&#xff0c;再直接去推敲如何写算法 1.1 排序算法 1.1.1 冒泡排序 每次从数组中找出最大值放在数组的后面去 public class demo {public static void main(S…

分析开源机器学习框架TensorFlow

TensorFlow是一个开源的机器学习框架&#xff0c;由Google开发和维护。它提供了一个灵活的编程环境&#xff0c;可用于构建和训练各种机器学习模型。TensorFlow的基本概念和使用场景如下&#xff1a; 张量&#xff08;Tensor&#xff09;&#xff1a;在TensorFlow中&#xff0c…

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

事件传参&#xff1a;在触发事件时,将一些数据作为参数传递给事件处理函数的过程,就是事件传参。 在微信小程序中,我们经常会在组件上添加一些自定义数据,然后在事件处理函数中获取这些自定义数据,从而完成业务逻辑的开发。 在组件上通过data-"的方式定义需要传递的数据,其…

大屏 超宽屏自适应最优解决方案(transform:scale)

问题引入&#xff1a; 可视化数据大屏需要适配各种大屏尺寸 1080P&#xff1a;1920*1080 2K&#xff1a;2560*1440 左右 4K&#xff1a;3840*2160 左右 8K&#xff1a;7680*4320 左右 ① 大屏使用rem 耗时 而且对浏览器最小字体不支持&#xff0c; ② 使用transform:scale可以…

【unity】shader优化总结-转载

分为三个部分&#xff1a;Unity官方文档&#xff0c;GDC&#xff0c;个人经验。 Unity Manual 1.计算量优化。着色器进行的计算和处理越多&#xff0c;对性能的影响越大。针对不影响最终效果但依然进行计算的无效代码&#xff0c;进行移除操作。计算的频率也会影响游戏的性能…

《操作系统真相还原》读书笔记四:安装nasm

下载链接&#xff1a;https://www.nasm.us/pub/nasm/releasebuilds/2.13.03/ 下载-解压-安装 tar zxvf nasm-2.13.03.tar.gz ./configure --prefix/home/truthos/nasm/toolchain/make && makeinstall执行make install export PATH/home/truthos/nasm/toolchain/bin:…

用自适应K-Means的差分进化算法解决有容量的电动汽车(EV)路由问题(2023)

Solving the Capacitated Electric Vehicle (EV) Routing Problem by The Differential Evolutionary Algorithm with Adaptive K-Means 摘要 本文旨在解决限制电能和工作量的路由问题&#xff0c;称为电容式电动汽车路由问题&#xff08;CEVRP&#xff09;。这个问题的目的是…

《 前端挑战与未来:如何看待“前端已死”》

在技术领域,时常会有一些激进的言论引发热议,比如近年来不少人声称“前端已死”。这样的言论引发了广泛的讨论和反思。本文将从几个方向探讨这个话题:为什么会出现“前端已死”的言论、如何看待这种说法、前端技术的未来发展趋势以及前端人如何应对这场职位突围战。 为什么会…

代码训练LeetCode(1)合并有序数组详解

代码训练(1)LeetCode之合并两个有序数组 Author: Once Day Date: 2024年3月5日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 88. 合并两个有序数组 - 力扣&#xff08;LeetCode&#xff09;力扣 (LeetCode) …

【BUG】Windows状态栏总卡死解决办法

屋漏偏逢连夜雨&#xff0c;正在赶deadline呢&#xff0c;Windows状态老卡死&#xff0c;一时间崩溃。 解决办法&#xff1a; 右键状态栏新闻和咨询关掉 这个烧笔新闻与资讯我真服了

Ubantu 18.04 如何映射IP到公网,外网可以访问

介绍一种简单的方式&#xff0c;就是通过路由侠 inux 系统安装路由侠&#xff0c;可通过两种方式进行&#xff0c;一种是通过直接脚本安装&#xff0c;一种是通过 Docker 安装。 windows下载地址&#xff1a;路由侠-局域网变公网 方式一&#xff1a;通过脚本安装 1、获取安…