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上的问题,还能将这种思想应用到实际开发中的各种场景。

相关推荐
Pluchon2 小时前
硅基计划4.0 算法 BFS最短路问题&多源BFS&拓扑排序
java·算法·哈希算法·近邻算法·广度优先·宽度优先·迭代加深
Kratzdisteln2 小时前
【Web-Crawler-Steamdt】以项目文件steamdt_crawler.py学习python爬虫
爬虫·python·学习
秋刀鱼 ..2 小时前
2025年第二届智能制造与自动化国际研讨会(ISIMA 2025)
运维·人工智能·python·自动化·能源·制造
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于Java的星星儿童救助帮扶系统为例,包含答辩的问题和答案
java·开发语言
清晓粼溪2 小时前
SpringBoot3-02:整合资源
java·开发语言·spring boot
CoderYanger2 小时前
C.滑动窗口-求子数组个数-越短越合法——3134. 找出唯一性数组的中位数
java·开发语言·数据结构·算法·leetcode
谷粒.2 小时前
云原生测试:在分布式系统中的质量保障策略
运维·python·测试工具·云原生·架构·自动化·测试覆盖率
彭泽布衣2 小时前
python 使用openssl时,遇到ValueError: unsupported hash type sha1异常等问题
python·openssl版本兼容性·python sha异常