LeetCode 56. 给出一个区间的集合,请合并所有重叠的区间。
示例
// 示例1
输入: [[1,3], [2,6], [8,10], [15,18]]
输出: [[1,6], [8,10], [15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠,将它们合并为 [1,6]。
// 示例2
输入: [[1,4], [4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
解法思路
假设有区间 a 和 b,区间 a 的起始时间要早于 b 的起始时间。那么它们之间有如下 3 种可能会出现的情况。
- 两个区间没有任何重叠的部分,因此区间不会发生融合。
- 两个区间有重叠。
- 新区间的起始时间是 a 的起始时间,这个不变。
- 终止时间为a终止时间和b终止时间的最大值。
javaScript 代码实现
/**
* @param {number[][]} intervals
* @return {number[][]}
*/
var merge = function(intervals) {
// 排序: 按从小到大
intervals.sort((a, b) => a[0] - b[0])
// 前一个区间,后一个区间, 存储结果
let previous, current, result = [];
for (let i = 0; i < intervals.length; i++) {
current = intervals[i]
// 是第一个区间或者当前区间和前一个区间没有重叠,将当前区间加入到结果中
if (!previous || current[0] > previous[1]) {
// 将当前区间赋值给前一个区间
previous = current
result.push(current)
} else {
// 否则的话,就是两个区间发生了重叠
// 更新前一个区间的结束时间
previous[1] = Math.max(previous[1], current[1])
}
}
return result;
};
LeetCode 435:给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
- 可以认为区间的终点总是大于它的起点。
- 区间
[1,2]
和[2,3]
的边界相互“接触”,但没有相互重叠。
示例
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
暴力解法
解法思路
- 如果当前区间和前一个区间没有发生重叠,则尝试保留当前区间,表明此处不需要删除操作。
- 题目要求最少的删除个数,只有在这样的情况下,才不需要做任何删除操作。
- 在这种情况下,虽然两个区间没有重叠,但是也要考虑尝试删除当前区间的情况。
- 对比哪种情况所需要删除的区间最少。
javaScript 代码实现
/**
* @param {number[][]} intervals
* @return {number}
*/
var eraseOverlapIntervals = function(intervals) {
// 排序:从小到大
intervals.sort((a, b) => a[0] - b[0])
// 递归处理每个区间
return eraseOverlapIntervalsRe(-1, 0, intervals)
};
function eraseOverlapIntervalsRe(prev, cur, intervals) {
// 已经处理完所有区间,表明不需要删除的区间,直接返回
if (cur === intervals.length) {
return 0
}
let take = Number.MAX_SAFE_INTEGER, notake;
// 只有当prev, curr没有发生重叠的时候,才可以选择保留当前的区间curr
if (prev === -1 || intervals[prev][1] <= intervals[cur][0]) {
take = eraseOverlapIntervalsRe(cur, cur + 1, intervals)
}
// 其他情况,删除当前区间, 继续看删除之后有什么结果
notake = eraseOverlapIntervalsRe(prev, cur + 1, intervals) + 1
return Math.min(take, notake)
}
贪婪法
解法思路
要尽可能少得删除区间,那么当遇到了重叠的时候,应该把区间跨度大,即结束比较晚的那个区间删除。因为如果不删除它,它会和剩下的其他区间发生重叠的可能性非常大。
实现代码
/**
* @param {number[][]} intervals
* @return {number}
*/
var eraseOverlapIntervals = function(intervals) {
if (intervals.length === 0) return 0
// 排序:从小到大
intervals.sort((a, b) => a[0] - b[0])
// 用一个变量 end 记录当前的最小结束时间点
// 用count记录到目前为止删除了多少区间
let end = intervals[0][1], count = 0;
// 从第二个区间开始遍历,判断当前区间和前一个区间的结束时间
for (let i = 1; i < intervals.length; i++) {
// 当前区间和前一个区间有重叠,即当前区间的起始时间小区前一个时间的结束时间,end记录下两个结束时间的最小值,把结束时间晚的区间删除,计数加1。
if (intervals[i][0] < end) {
end = Math.min(end, intervals[i][1]);
count++;
} else {
end = intervals[i][1]
}
}
// 如果没有发生重叠,根据贪婪法,更新 end 变量为当前区间的结束时间
return count
};