LeetCode 56.合并区间 Java

区间合并问题详解:排序与贪心策略

题目描述

给定一个区间集合,其中每个区间用一对整数 [start, end] 表示,任务是合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需要恰好覆盖原始区间集合中的所有区间。

示例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. 排序是前提

如果不先对区间进行排序,我们将无法系统性地判断哪些区间可能重叠。想象一下,如果区间是乱序的,我们可能需要多次比较才能确定最终的合并结果。

2. 合并策略

排序后,我们可以按顺序遍历区间,每次将当前区间与已合并区间列表中的最后一个区间进行比较。这样只需要一次遍历就能完成所有合并操作。

详细解法步骤

第一步:区间排序

java 复制代码
// 1.对intervals进行排序(按照子数组的起始做排序)
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

这里使用Java的Arrays.sort方法,通过自定义比较器按照每个区间的起始位置进行升序排序。这是整个算法的基础。

第二步:遍历与合并

java 复制代码
// 2.遍历排序后的intervals
// 2.1 定义一个数组,用来存储合并后的区间
List<int[]> merged = new ArrayList<>();
for(int i=0;i<intervals.length;i++){
    int start = intervals[i][0];
    int end = intervals[i][1];
    
    //如果是第一次来,或者此时的这个区间与merged的最后一个区间没有重叠
    if(merged.isEmpty()||merged.get(merged.size()-1)[1] < start){
        merged.add(new int[]{start,end});
    }else{
        //有重叠,更新merged的最后一个区间的结束位置end
        merged.get(merged.size()-1)[1] = Math.max(intervals[i][1],merged.get(merged.size()-1)[1]);
    }
}

关键判断条件解析:

  • 无重叠情况:当前区间的起始位置 > 已合并区间列表中最后一个区间的结束位置
  • 有重叠情况:当前区间的起始位置 ≤ 已合并区间列表中最后一个区间的结束位置

当检测到重叠时,我们只需要更新已合并区间列表中最后一个区间的结束位置,取两个区间结束位置的较大值。

完整代码实现

java 复制代码
class Solution {
    /**
     * 思路:
     * 1. 先对区间数组进行排序,不排序,无法进行区间合并
     * 2. 准备一个merged来存储合并后的区间,遍历所有区间,比较当前区间与merged中的最后一个区间是否有重叠(重叠的判断:就是这个区间的开头 <= 另外一个区间的结尾)
     *    有重叠则更新merged中最后一个区间的结尾,这个就完成了区间的合并,还保证了不会遗漏
     * @param intervals
     * @return
     */
    public int[][] merge(int[][] intervals) {
        // 1.对intervals进行排序(按照子数组的起始做排序)
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

        // 2.遍历排序后的intervals
        // 2.1 定义一个数组,用来存储合并后的区间
        List<int[]> merged = new ArrayList<>();
        for(int i=0;i<intervals.length;i++){
            int start = intervals[i][0];
            int end = intervals[i][1];

            //如果是第一次来,或者此时的这个区间与merged的最后一个区间没有重叠
            if(merged.isEmpty()||merged.get(merged.size()-1)[1] < start){
                merged.add(new int[]{start,end});
            }else{
                //有重叠,更新merged的最后一个区间的结束位置end
                merged.get(merged.size()-1)[1] = Math.max(intervals[i][1],merged.get(merged.size()-1)[1]);
            }
        }

        //返回结果
        return merged.toArray(new int[merged.size()][]);
    }
}

算法复杂度分析

  • 时间复杂度:O(n log n),其中排序操作需要 O(n log n) 时间,遍历区间需要 O(n) 时间
  • 空间复杂度:O(log n) 或 O(n),取决于排序算法的实现。Java中Arrays.sort()使用TimSort,空间复杂度为O(log n)到O(n)。此外,结果存储需要O(n)空间

关键点总结

  1. 排序的必要性:排序使得我们可以按顺序处理区间,确保不会遗漏任何可能的重叠
  2. 贪心策略:每次只考虑当前区间与已合并区间列表中的最后一个区间,这种局部最优选择能导致全局最优解
  3. 重叠判断 :理解 当前区间起始 ≤ 前一个区间结束 是判断重叠的关键条件
  4. 边界处理 :注意区间起始和结束的包含关系,特别是当 [1,4][4,5] 这种情况时,它们被认为是重叠的

扩展思考

  1. 如果区间是已经排序的,可以如何优化算法?
  2. 如果要求合并后的区间按照某种特定顺序排列,如何处理?
  3. 如果区间数量非常大,无法一次性加载到内存中,应该如何解决?

实际应用场景

区间合并算法在实际中有很多应用:

  • 日程安排和会议室的预订
  • 磁盘空间的分配与回收
  • 网络带宽的时间段分配
  • 任务调度和时间线管理

通过理解和掌握这个算法,你不仅能够解决LeetCode上的问题,还能将这种思想应用到实际开发中的各种场景。

相关推荐
6Hzlia几秒前
【Hot 100 刷题计划】 LeetCode 72. 编辑距离 | C++ 经典 DP 增删改状态转移
c++·算法·leetcode
穿条秋裤到处跑1 分钟前
每日一道leetcode(2026.04.16):距离最小相等元素查询
算法·leetcode·职场和发展
赵优秀一一4 分钟前
SQLAlchemy学习记录
开发语言·数据库·python
m0_640309306 分钟前
如何在phpMyAdmin中生成XML格式导出_与其他企业系统的数据交互
jvm·数据库·python
2301_8152795212 分钟前
JavaScript中快速清空数组的多种方式及其性能差异
jvm·数据库·python
码农周20 分钟前
告别大体积PDF!基于PDFBox的Java压缩工具
java·spring boot
捉鸭子28 分钟前
海关总署瑞数vmp算法还原
python·网络安全·node.js·网络爬虫
devilnumber29 分钟前
java中Redisson ,jedis,Lettuce和Spring Data Redis的四种深度对比和优缺点详解
java·redis·spring
摇滚侠30 分钟前
Java 进阶教程,全面剖析 Java 多线程编程
java·开发语言
yaaakaaang31 分钟前
十四、命令模式
java·命令模式