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 双指针法的基本逻辑解决这个问题的核心在于利用两个指针分别遍历两个区间列表。具体步骤如下初始化指针i和j分别指向两个列表的起始位置比较当前两个区间的起始和结束位置计算可能存在的交集区间移动结束位置较小的那个区间的指针重复上述过程直到任一列表遍历完毕关键点在于如何正确计算两个区间的交集。数学上两个区间[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语言需要手动管理内存这里有几个关键注意事项预先分配足够大的结果数组通常是两个列表大小之和为每个交集区间单独分配内存记得为returnColumnSizes分配内存调用者需要负责释放这些内存一个常见的错误是忘记为returnColumnSizes分配内存这会导致运行时错误。另一个陷阱是结果数组预分配过大造成内存浪费或者过小导致越界。4. 复杂度分析与优化4.1 时间复杂度该算法的时间复杂度是O(mn)其中m和n分别是两个列表的长度。这是因为每个指针最多移动mn次每次操作都是常数时间。4.2 空间复杂度空间复杂度也是O(mn)最坏情况下需要存储所有可能的交集区间。在实际应用中如果交集很少可以考虑动态调整内存分配策略但这会增加代码复杂度。5. 实际应用场景这类区间交集问题在实际开发中有广泛应用会议系统查找多个参与者的共同空闲时间资源调度确定设备可用的重叠时间段基因组学查找DNA序列的重叠区域日志分析找出多个服务同时出现异常的时段理解这个算法不仅能帮助通过面试更能为解决实际问题提供思路。例如在处理用户行为日志时我经常需要找出多个事件序列的共同发生时段这时类似的区间处理技巧就派上用场了。6. 常见错误与调试技巧6.1 典型错误案例在实现这个算法时我遇到过几个典型的bug指针移动逻辑错误错误地总是移动第一个指针交集判断条件错误遗漏了a2 b1的条件内存分配不足没有预分配足够的结果空间忘记设置returnColumnSizes的值6.2 调试建议对于这类问题我建议使用以下测试用例进行验证常规情况输入[[1,3],[5,9]] 和 [[2,5],[7,10]]预期输出[[2,3],[5,5],[7,9]]无交集情况输入[[1,3],[5,7]] 和 [[8,10]]预期输出[]完全包含情况输入[[1,7]] 和 [[3,5]]预期输出[[3,5]]单点区间输入[[1,1],[3,3]] 和 [[1,3]]预期输出[[1,1],[3,3]]在LeetCode上提交前务必在本地用这些测试用例验证你的代码。特别是对于C语言实现内存错误往往不会立即导致程序崩溃但会在评测时产生不可预测的结果。7. 扩展思考7.1 变种问题掌握了基础解法后可以尝试解决一些变种问题处理未排序的区间列表需要先排序合并多个区间列表的交集计算交集的持续总时间找出满足特定条件的最长交集7.2 性能优化对于特别大的区间列表可以考虑以下优化提前终止当剩余区间不可能再有交集时提前结束并行处理将列表分段后并行计算区间压缩预处理时合并相邻或重叠区间不过在实际面试中通常只需要实现基础解法即可除非特别说明有性能要求。8. 编码风格建议在C语言实现这类算法题时良好的编码风格很重要为指针操作添加注释合理命名变量如用i,j作指针a1/a2表示区间端点保持函数单一职责不要在一个函数里做太多事情添加必要的空行分隔逻辑块为复杂条件添加解释性注释例如交集判断条件可以这样注释// 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 resJava则需要处理更多的样板代码但思路相同。相比之下C语言版本虽然更冗长但执行效率通常更高也更能体现对内存管理的掌握程度。10. 学习路径建议要彻底掌握这类区间问题我建议的学习路径是先理解基础的双指针概念如合并两个有序数组练习简单的区间问题如合并区间解决本题区间交集尝试更复杂的变种如区间并集、区间覆盖等在实际项目中寻找应用场景LeetCode上有一个完整的区间问题合集按难度排序非常适合系统性地练习。我个人的经验是至少要做10道左右的区间问题才能对各种边界条件形成条件反射。