最近在准备华为OD机考的过程中,遇到了一道关于区间交集的题目。这道题在双机位监考的C卷中出现,要求用Java实现一个高效的解决方案。作为过来人,我想把这道题的解题思路和实际编码中的注意事项分享给大家。
区间问题是算法面试中的常客,特别是在处理时间调度、资源分配等场景时非常实用。这道题的核心是给定两组区间,找出所有存在交集的区间对。听起来简单,但在限时编程环境下,如何写出既正确又高效的代码还是很有挑战性的。
给定两个区间集合A和B,每个区间表示为[start, end]。需要找出所有满足A[i]与B[j]有交集的区间对(i,j)。交集的定义是两个区间有至少一个公共整数点。
例如:
A = [[1,4], [5,8]]
B = [[3,6], [7,9]]
输出应该是 [[1,4,3,6], [5,8,3,6], [5,8,7,9]]
最直观的方法是双重循环遍历所有区间对,检查是否有交集。这种方法时间复杂度O(n*m),在区间数量较大时性能会很差。
更优的解法是利用区间已经排序的特性(题目通常保证输入有序),采用双指针法可以将时间复杂度优化到O(n+m)。这是面试官期望看到的解法。
双指针法的关键在于利用区间有序的特性,通过指针的移动跳过不可能产生交集的区间。基本步骤如下:
java复制public List<List<Integer>> intervalIntersection(int[][] A, int[][] B) {
List<List<Integer>> result = new ArrayList<>();
int i = 0, j = 0;
while (i < A.length && j < B.length) {
// 计算两个区间的交集
int start = Math.max(A[i][0], B[j][0]);
int end = Math.min(A[i][1], B[j][1]);
// 如果存在交集
if (start <= end) {
result.add(Arrays.asList(A[i][0], A[i][1], B[j][0], B[j][1]));
}
// 移动指针
if (A[i][1] < B[j][1]) {
i++;
} else {
j++;
}
}
return result;
}
两个区间[a1, a2]和[b1, b2]有交集的条件是:
max(a1, b1) <= min(a2, b2)
这个条件涵盖了所有可能的相交情况:
为什么总是移动end值较小的指针?因为较大的end值可能还会与下一个区间产生交集。例如:
A[i] = [1,5]
B[j] = [3,7]
B[j+1] = [6,9]
移动i指针后,A[i+1]可能与B[j]还有交集
在实际机考中,输入可能是字符串形式,需要先解析成二维数组。例如输入可能是:
"[[1,4],[5,8]] [[3,6],[7,9]]"
解析代码示例:
java复制String[] parts = input.split(" ");
int[][] A = parseIntervals(parts[0]);
int[][] B = parseIntervals(parts[1]);
private int[][] parseIntervals(String s) {
s = s.replaceAll("\\[|\\]", "");
String[] nums = s.split(",");
int[][] res = new int[nums.length/2][2];
for (int i = 0; i < nums.length; i+=2) {
res[i/2][0] = Integer.parseInt(nums[i]);
res[i/2][1] = Integer.parseInt(nums[i+1]);
}
return res;
}
双指针法每个指针最多移动n或m次,因此时间复杂度是O(n+m),比暴力解法的O(n*m)有显著提升。
空间复杂度是O(1)(不考虑输出结果的空间),因为只使用了固定数量的指针变量。
好的测试用例应该包含:
示例测试用例:
java复制@Test
public void testIntervalIntersection() {
// 正常情况
int[][] A1 = {{1,4}, {5,8}};
int[][] B1 = {{3,6}, {7,9}};
// 预期输出: [[1,4,3,6], [5,8,3,6], [5,8,7,9]]
// 无交集情况
int[][] A2 = {{1,2}};
int[][] B2 = {{3,4}};
// 预期输出: []
// 一个区间包含另一个
int[][] A3 = {{1,10}};
int[][] B3 = {{2,3}, {4,5}};
// 预期输出: [[1,10,2,3], [1,10,4,5]]
}
华为OD机考采用双机位监考,意味着:
建议:
虽然机考主要考察算法正确性,但良好的代码风格会加分:
在无法使用IDE调试的情况下:
java复制System.out.println("i="+i+" j="+j+" A[i]="+Arrays.toString(A[i])+" B[j]="+Arrays.toString(B[j]));
优化后的代码示例:
java复制public List<List<Integer>> intervalIntersectionOptimized(int[][] A, int[][] B) {
List<List<Integer>> result = new ArrayList<>(Math.min(A.length, B.length));
int i = 0, j = 0;
while (i < A.length && j < B.length) {
int aStart = A[i][0], aEnd = A[i][1];
int bStart = B[j][0], bEnd = B[j][1];
// 计算交集
int start = aStart > bStart ? aStart : bStart;
int end = aEnd < bEnd ? aEnd : bEnd;
if (start <= end) {
List<Integer> intersection = new ArrayList<>(4);
intersection.add(aStart);
intersection.add(aEnd);
intersection.add(bStart);
intersection.add(bEnd);
result.add(intersection);
}
// 移动指针
if (aEnd < bEnd) {
i++;
} else {
j++;
}
}
return result;
}
在准备华为OD机考的过程中,我发现区间类题目有几个关键点需要特别注意:
在双机位监考环境下,保持冷静很重要。遇到问题时:
这道区间交集题目很好地考察了候选人对双指针算法的理解和应用能力。掌握这类问题的解法不仅对通过机考有帮助,在实际开发中处理时间区间、资源调度等问题时也非常实用。