二分法 + 双指针 + 逆向思维
前言
区间列表的交集关键问题在于两个区间的关系判断,可以逆向思维来进行关系分类处理;该题还可以练习二分法和双指针法。
一、区间列表的交集
二、区间关系分类
1、双指针+逆向思维
// 二分其实也没太必要。
class IntervalIntersection2 {
/*
target:找两个区间数组重叠的区间。两个区间数组各自有序。
有序区间,可二分查找第一个区间数组的第一个区间在第二个区间数组的位置。
然后双指针判断重合区间,知道一个指针指到了一个区间数组的末尾。
如何判断区间的关系?找到存在区间重叠的情况,就能取两者重叠区间。
*/
public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
// 取两个数组的长度。
int m = firstList.length, n = secondList.length;
// 一旦一个数组为空,则肯定没有重合区间。
if (m == 0 || 0 == n) return new int[][]{};
// 初始化两个区间数组的指针。
int fp = 0, sp = 0;
List<int[]> rs = new ArrayList<>();
while (fp < m && n > sp) {
// 区间关系,分类处理。
// 存在重叠区间。处理方式,因为不存在重叠区间比较好选择,所以! + 不存在区间的情况 == 逆向思维。
if (!(firstList[fp][1] < secondList[sp][0] || firstList[fp][0] > secondList[sp][1])) {
int front = firstList[fp][0];
if (front < secondList[sp][0]) front = secondList[sp][0];
int behind = firstList[fp][1];
if (behind > secondList[sp][1]) behind = secondList[sp][1];
rs.add(new int[]{front, behind});
}
// 更新fp 和 sp。
if (firstList[fp][1] > secondList[sp][1]) ++sp;
else ++fp;
}
return rs.toArray(new int[0][]);
}
}
2、二分提速+双指针+逆向思维
// 区间列表的交集。
public class IntervalIntersection {
/*
target:找两个区间数组重叠的区间。两个区间数组各自有序。
有序区间,可二分查找第一个区间数组的第一个区间在第二个区间数组的位置。
然后双指针判断重合区间,知道一个指针指到了一个区间数组的末尾。
如何判断区间的关系?找到存在区间重叠的情况,就能取两者重叠区间,区间重叠判断情况太多,可逆向思维判断。
*/
public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
// 取两个数组的长度。
int m = firstList.length, n = secondList.length;
// 一旦一个数组为空,则肯定没有重合区间。
if (m == 0 || 0 == n) return new int[][]{};
// 初始化两个区间数组的指针。
int fp = 0, sp = binarySearch(secondList, firstList[0][0]);
List<int[]> rs = new ArrayList<>();
while (fp < m && n > sp) {
// 区间关系,分类处理。
// 存在重叠区间。处理方式,因为不存在重叠区间比较好选择,所以! + 不存在区间的情况 == 逆向思维。
if (!(firstList[fp][1] < secondList[sp][0] || firstList[fp][0] > secondList[sp][1])) {
int front = firstList[fp][0];
if (front < secondList[sp][0]) front = secondList[sp][0];
int behind = firstList[fp][1];
if (behind > secondList[sp][1]) behind = secondList[sp][1];
rs.add(new int[]{front, behind});
}
// 更新fp 和 sp。
if (firstList[fp][1] > secondList[sp][1]) ++sp;
else ++fp;
}
return rs.toArray(new int[0][]);
}
// 插入式二分,插左。找target在arr[][1]的位置。
private int binarySearch(int[][] arr, int target) {
int low = 0, high = arr.length;
while (low < high) {
int mid = low + (high - low >>> 1);
int midVal = arr[mid][1];
if (target > midVal) low = mid + 1;
else high = low;
}
return high;
}
}
总结
1)二插入插左法。
2)双指针。
3)逆向思维解决区间关系分类。
参考文献
[1] LeetCode 区间列表的交集