手摸手提桶跑路——LeetCode56. 合并区间

161 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

题目描述

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 

来源:力扣(LeetCode) 链接:leetcode.cn/problems/me…

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3][2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示: 

  • 1 <= intervals.length <= 104 

  • intervals[i].length == 2 

  • 0 <= starti <= endi <= 104

解题思路

首先我们来理解一下题意。

不重叠的情况

假设有两个区间 [0,5] 和 [6,8],相当于 [0,1,2,3,4,5] 和 [6,7,8],可以看出它们两个是没有交集的。因此两个区间的不重叠区间数组为 [0,1,2,3,4,5,6,7,8],也就是[0,5] 和 [6,8]。

对于不重叠的情况来说,我们需要返回的数组应该是[区间1, 区间2]。

全部重叠的情况

假设有两个区间 [0,5] 和 [1,3],相当于 [0,1,2,3,4,5],两个区间的交集就是[1,2,3],也就是说[1,3] 是被 [0,5] 全包含的,最终它们的不重叠区间数组为 [0,1,2,3,4,5],也就是[0,5]。

对于全部重叠的情况来说,我们需要返回的数组应该是[表示范围更广的那个区间]。

部分重叠的情况

假设有两个区间 [0,5] 和 [2,7],相当于 [0,1,2,3,4,5,6,7],它们的交集是 [2,3,4,5],但是区间2的结尾是比区间1多了 [6,7],是更广的一个范围,所以他们的不重叠区间数组应该是 [0,1,2,3,4,5,6,7],也就是[0,7]。

对于部分重叠情况来说,我们需要返回的数组应该是[起始更早的区间的start,结束更晚的区间的end]。

有了这三部分的逻辑,我们就可以开始着手解题了。

题解

为了使得计算方便,我们可以对所有区间的start进行从小到大的排序,这样做的好处:

① 另一个区间的 start 是否大于当前区间的 end,如果是,则说明是两个不重叠的区间。

② 另一个区间的 start 小于当前区间的end,那么它可能是全部重叠,也可能是部分重叠。如果另一个区间的end也小于当前区间的end的话,则说明另一个区间和当前的区间全部重叠,否则就说明是部分重叠。

我们根据这个逻辑就可以写出相应的程序了。

/** 
* @param {number[][]} intervals 
* @return {number[][]} 
*/
var merge = function(intervals) {
    intervals = intervals.sort((a, b)=>a[0]-b[0]);
    return intervals.reduce((f, c) => {
        const lens = f.length-1, last = f[lens];
        if(!last) {
            return [c]; // 首个区间必然是直接作为结果数组的其中一个元素了
        }
        if(c[0] > last[1]) { // 如果当前区间的 start 大于先前区间的end,则说明不重叠,直接push
            f.push(c);
        } else if(c[1] > last[1]) {
           // 否则就是部分重叠,取[起始更早的区间的start,结束更晚的区间的end]
            f[lens] = [last[0], c[1]];
        }
        return f;
    }, []);
};