排序题目:合并区间

题目

标题和出处

标题:合并区间

出处:56. 合并区间

难度

5 级

题目描述

要求

给定区间数组 intervals \texttt{intervals} intervals,其中 intervals[i]   =   [start i ,   end i ] \texttt{intervals[i] = [start}_\texttt{i}\texttt{, end}_\texttt{i}\texttt{]} intervals[i] = [starti, endi],合并所有重叠的区间,并返回一个不重叠的区间数组,该数组覆盖输入中的所有区间。

示例

示例 1:

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

示例 2:

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

数据范围

  • 1 ≤ intervals.length ≤ 10 4 \texttt{1} \le \texttt{intervals.length} \le \texttt{10}^\texttt{4} 1intervals.length104
  • intervals[i].length = 2 \texttt{intervals[i].length} = \texttt{2} intervals[i].length=2
  • 0 ≤ start i ≤ end i ≤ 10 4 \texttt{0} \le \texttt{start}_\texttt{i} \le \texttt{end}_\texttt{i} \le \texttt{10}^\texttt{4} 0startiendi104

解法

思路和算法

为了合并数组 intervals \textit{intervals} intervals 中的所有重叠的区间,需要首先将所有的区间排序,然后判断是否有重叠的区间,并将重叠的区间合并。

对区间排序的方法是,首先将区间按照开始位置升序排序,如果存在多个区间的开始位置相同,则按照结束位置降序排序。排序之后,相同开始位置的多个区间中,第一个区间是最长的区间,对于每个开始位置只需要保留最长的区间。

由于无法事先知道合并后剩余多少个区间,因此使用列表存储合并后的区间。遍历排序后的数组 intervals \textit{intervals} intervals,首先将首个区间(即 intervals [ 0 ] \textit{intervals}[0] intervals[0])添加到列表中,然后对其余的区间依次判断是否需要和已有的区间合并,并更新列表。

由于相同开始位置的多个区间只保留最长的区间,因此对于遍历到的每个区间,需要判断该区间与数组 intervals \textit{intervals} intervals 中的上一个区间的开始位置是否相同,如果相同则跳过。当前区间不跳过时,将当前区间记为 curr \textit{curr} curr,将列表中的最后一个区间记为 prev \textit{prev} prev。由于已经对数组 intervals \textit{intervals} intervals 排序,因此 prev \textit{prev} prev 的开始位置一定小于 curr \textit{curr} curr 的开始位置。比较 curr \textit{curr} curr 的开始位置和 prev \textit{prev} prev 的结束位置,执行如下操作。

  • 如果 curr \textit{curr} curr 的开始位置小于等于 prev \textit{prev} prev 的结束位置,则 prev \textit{prev} prev curr \textit{curr} curr 重叠,需要合并,合并之后的区间的开始位置是 prev [ 0 ] \textit{prev}[0] prev[0](因为 prev [ 0 ] < curr [ 0 ] \textit{prev}[0] < \textit{curr}[0] prev[0]<curr[0]),结束位置是 max ⁡ ( prev [ 1 ] , curr [ 1 ] ) \max(\textit{prev}[1], \textit{curr}[1]) max(prev[1],curr[1])。更新 prev [ 1 ] \textit{prev}[1] prev[1] 的值,即可实现合并,不需要将 curr \textit{curr} curr 添加到列表中。

  • 如果 curr \textit{curr} curr 的开始位置大于 prev \textit{prev} prev 的结束位置,则 prev \textit{prev} prev curr \textit{curr} curr 不重叠,因此将 curr \textit{curr} curr 添加到列表中。

遍历结束之后,将列表转成数组返回。

代码

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            if (a[0] != b[0]) {
                return a[0] - b[0];
            } else {
                return b[1] - a[1];
            }
        });
        List<int[]> mergedList = new ArrayList<int[]>();
        mergedList.add(intervals[0]);
        int length = intervals.length;
        for (int i = 1; i < length; i++) {
            int[] curr = intervals[i];
            int[] prev = mergedList.get(mergedList.size() - 1);
            if (curr[0] == prev[0]) {
                continue;
            }
            if (curr[0] <= prev[1]) {
                prev[1] = Math.max(prev[1], curr[1]);
            } else {
                mergedList.add(curr);
            }
        }
        return mergedList.toArray(new int[mergedList.size()][]);
    }
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是数组 intervals \textit{intervals} intervals 的长度。排序需要 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的时间,排序后遍历数组合并区间需要 O ( n ) O(n) O(n) 的时间,时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 intervals \textit{intervals} intervals 的长度。存储合并后的区间的列表需要 O ( n ) O(n) O(n) 的空间。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值