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

LeetCode 986题解:双指针法处理区间交集问题

1. 问题背景与核心挑战

LeetCode 986题"Interval List Intersections"是一个经典的区间处理问题,主要考察对有序区间的操作能力。题目给定两个已排序的区间列表,要求返回这两个列表中所有区间的交集集合。这类问题在实际开发中非常常见,比如处理日程安排冲突、资源分配重叠等场景。

我刚接触这道题时,第一反应是"这不就是双指针的变种吗?",但实际编码时发现边界条件的处理远比想象中复杂。特别是当区间存在多种重叠情况时,稍不注意就会漏判或者重复计算。举个例子,区间A[1,5]和区间B[3,7]的交集是[3,5],而A[1,3]和B[4,6]则没有交集。

2. 算法思路解析

2.1 双指针法的基本逻辑

解决这个问题的核心在于利用两个指针分别遍历两个区间列表。具体步骤如下:

  1. 初始化指针i和j,分别指向两个列表的起始位置
  2. 比较当前两个区间的起始和结束位置
  3. 计算可能存在的交集区间
  4. 移动结束位置较小的那个区间的指针
  5. 重复上述过程直到任一列表遍历完毕

关键点在于如何正确计算两个区间的交集。数学上,两个区间[a1, a2]和[b1, b2]的交集存在当且仅当a1 <= b2且b1 <= a2。如果存在交集,则交集区间为[max(a1,b1), min(a2,b2)]。

2.2 C语言实现细节

在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)); *returnSize = 0; int i = 0, j = 0; while(i < firstListSize && j < secondListSize){ int a1 = firstList[i][0], a2 = firstList[i][1]; int b1 = secondList[j][0], b2 = secondList[j][1]; // 检查是否有交集 if(a2 >= b1 && b2 >= a1){ // 计算交集 int start = a1 > b1 ? a1 : b1; int end = a2 < b2 ? a2 : b2; // 存储结果 result[*returnSize] = malloc(sizeof(int)*2); result[*returnSize][0] = start; result[*returnSize][1] = end; (*returnColumnSizes)[*returnSize] = 2; (*returnSize)++; } // 移动指针 if(a2 < b2) i++; else j++; } return result; }

3. 边界条件与特殊处理

3.1 空输入处理

在实际编码中,我们必须考虑以下几种边界情况:

  • 其中一个列表为空
  • 两个列表都为空
  • 列表中存在空区间(如[3,3]表示单个点)

在C语言实现中,对空输入的处理尤为重要。例如,当firstListSize为0时,我们应该立即返回空数组,而不是继续执行后续逻辑。

3.2 内存管理要点

C语言需要手动管理内存,这里有几个关键注意事项:

  1. 预先分配足够大的结果数组(通常是两个列表大小之和)
  2. 为每个交集区间单独分配内存
  3. 记得为returnColumnSizes分配内存
  4. 调用者需要负责释放这些内存

一个常见的错误是忘记为returnColumnSizes分配内存,这会导致运行时错误。另一个陷阱是结果数组预分配过大造成内存浪费,或者过小导致越界。

4. 复杂度分析与优化

4.1 时间复杂度

该算法的时间复杂度是O(m+n),其中m和n分别是两个列表的长度。这是因为每个指针最多移动m+n次,每次操作都是常数时间。

4.2 空间复杂度

空间复杂度也是O(m+n),最坏情况下需要存储所有可能的交集区间。在实际应用中,如果交集很少,可以考虑动态调整内存分配策略,但这会增加代码复杂度。

5. 实际应用场景

这类区间交集问题在实际开发中有广泛应用:

  1. 会议系统:查找多个参与者的共同空闲时间
  2. 资源调度:确定设备可用的重叠时间段
  3. 基因组学:查找DNA序列的重叠区域
  4. 日志分析:找出多个服务同时出现异常的时段

理解这个算法不仅能帮助通过面试,更能为解决实际问题提供思路。例如,在处理用户行为日志时,我经常需要找出多个事件序列的共同发生时段,这时类似的区间处理技巧就派上用场了。

6. 常见错误与调试技巧

6.1 典型错误案例

在实现这个算法时,我遇到过几个典型的bug:

  1. 指针移动逻辑错误:错误地总是移动第一个指针
  2. 交集判断条件错误:遗漏了a2 >= b1的条件
  3. 内存分配不足:没有预分配足够的结果空间
  4. 忘记设置returnColumnSizes的值

6.2 调试建议

对于这类问题,我建议使用以下测试用例进行验证:

  1. 常规情况:

    • 输入:[[1,3],[5,9]] 和 [[2,5],[7,10]]
    • 预期输出:[[2,3],[5,5],[7,9]]
  2. 无交集情况:

    • 输入:[[1,3],[5,7]] 和 [[8,10]]
    • 预期输出:[]
  3. 完全包含情况:

    • 输入:[[1,7]] 和 [[3,5]]
    • 预期输出:[[3,5]]
  4. 单点区间:

    • 输入:[[1,1],[3,3]] 和 [[1,3]]
    • 预期输出:[[1,1],[3,3]]

在LeetCode上提交前,务必在本地用这些测试用例验证你的代码。特别是对于C语言实现,内存错误往往不会立即导致程序崩溃,但会在评测时产生不可预测的结果。

7. 扩展思考

7.1 变种问题

掌握了基础解法后,可以尝试解决一些变种问题:

  1. 处理未排序的区间列表(需要先排序)
  2. 合并多个区间列表的交集
  3. 计算交集的持续总时间
  4. 找出满足特定条件的最长交集

7.2 性能优化

对于特别大的区间列表,可以考虑以下优化:

  1. 提前终止:当剩余区间不可能再有交集时提前结束
  2. 并行处理:将列表分段后并行计算
  3. 区间压缩:预处理时合并相邻或重叠区间

不过在实际面试中,通常只需要实现基础解法即可,除非特别说明有性能要求。

8. 编码风格建议

在C语言实现这类算法题时,良好的编码风格很重要:

  1. 为指针操作添加注释
  2. 合理命名变量(如用i,j作指针,a1/a2表示区间端点)
  3. 保持函数单一职责(不要在一个函数里做太多事情)
  4. 添加必要的空行分隔逻辑块
  5. 为复杂条件添加解释性注释

例如,交集判断条件可以这样注释:

// Check if intervals overlap // a: [a1, a2], b: [b1, b2] // They overlap if a2 >= b1 && b2 >= a1 if(a2 >= b1 && b2 >= a1){ // ... }

这样的代码不仅更容易调试,也便于面试官理解你的思路。

9. 与其他语言的对比

虽然题目要求用C实现,但了解其他语言的解法也有助于加深理解:

Python可以利用列表推导简化代码:

def intervalIntersection(A, B): i = j = 0 res = [] while i < len(A) and j < len(B): a_start, a_end = A[i] b_start, b_end = B[j] # Calculate overlap start = max(a_start, b_start) end = min(a_end, b_end) if start <= end: res.append([start, end]) # Move pointer if a_end < b_end: i += 1 else: j += 1 return res

Java则需要处理更多的样板代码,但思路相同。相比之下,C语言版本虽然更冗长,但执行效率通常更高,也更能体现对内存管理的掌握程度。

10. 学习路径建议

要彻底掌握这类区间问题,我建议的学习路径是:

  1. 先理解基础的双指针概念(如合并两个有序数组)
  2. 练习简单的区间问题(如合并区间)
  3. 解决本题(区间交集)
  4. 尝试更复杂的变种(如区间并集、区间覆盖等)
  5. 在实际项目中寻找应用场景

LeetCode上有一个完整的区间问题合集,按难度排序,非常适合系统性地练习。我个人的经验是,至少要做10道左右的区间问题,才能对各种边界条件形成条件反射。

http://www.cnnetsun.cn/news/4076435.html

相关文章:

  • 从零拼出你的第一块数据大屏:DigitalTwinScreen 上手全记录
  • python的运筹学工业场景模拟第四十四篇:快递中转仓,多批次货物转运,中转仓容量限制,构建运输模型,求解转运分配。
  • 国际物流运费如何计算
  • 身体状态元素:人工个体动态建模的工程化路径
  • 基于SpringBoot的中华诗词文化交流平台的设计与实现
  • .NET高校学生管理系统开发实践与架构解析
  • lessmsi 快速实战:不安装软件也能完整提取 MSI 安装包内容
  • 从零搭建《饥荒联机版》本地专用服务器:硬件配置、网络部署与模组管理全攻略
  • 哈希查找:从原理到实践,掌握高效数据检索的核心技术
  • PyTorch预训练模型库:一站式下载、管理与调用方案
  • 网络工程师面试高频技术问题解析:静态路由、VLAN与RAID
  • Web代码安全防御实战:从注入漏洞到加密存储
  • 王者荣耀语音资源提取实战:从OBB解包到音频转换全流程解析
  • Agentic AI驾驶教练:基于反应器模型与Lingua Franca构建确定性CPS系统
  • 告别手抄截图:用YaeAchievement把原神成就数据导出做成一件5分钟小事
  • 网盘直链下载助手使用指南:八大网盘直链获取,从此告别龟速下载
  • 多智能体协作中KV-Cache通信优化与资源调度策略
  • WINDOWS系统文件SystemSupportInfo.dll丢失找不到问题解决
  • AI代码解释评估框架:从准确性到清晰度的多维度基准测试
  • HUD抬头显示技术全解析:从C-HUD到AR-HUD的原理、应用与选装指南
  • PyTorch神经网络入门实战:半小时手写代码跑通MNIST分类模型
  • Typora图片排版进阶:用HTML+CSS实现Flexbox与Grid布局
  • 几何A深度解析:从设计语言到三电系统,看未来汽车的务实探索
  • 多智能体协作中的Governed Memory架构:从内存治理到生产级实践
  • AI人格演化:基于大五模型与事件驱动的LLM智能体行为变化分析
  • PS V27.9深度解析:离线AI模型、本地部署与风险规避指南
  • CSS3 transform: scale() 原理、性能优化与实战应用全解析
  • 网盘直链获取工具完全上手指南:一个脚本覆盖八大主流云盘
  • 每日极客日报 · 2026年08月17日
  • 强化学习面试核心:从MDP到PPO/SAC的算法原理与工程实践