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

相关推荐
盐真卿14 分钟前
python2
java·前端·javascript
玄冥剑尊16 分钟前
贪心算法进阶
算法·贪心算法
玄冥剑尊18 分钟前
贪心算法深化 I
算法·贪心算法
52Hz11821 分钟前
力扣73.矩阵置零、54.螺旋矩阵、48.旋转图像
python·算法·leetcode·矩阵
iAkuya1 小时前
(leetcode)力扣100 二叉搜索树种第K小的元素(中序遍历||记录子树的节点数)
算法·leetcode·职场和发展
weixin_462446231 小时前
Python 使用 openpyxl 从 URL 读取 Excel 并获取 Sheet 及单元格样式信息
python·excel·openpyxl
一嘴一个橘子1 小时前
mybatis - 动态语句、批量注册mapper、分页插件
java
组合缺一1 小时前
Json Dom 怎么玩转?
java·json·dom·snack4
危险、1 小时前
一套提升 Spring Boot 项目的高并发、高可用能力的 Cursor 专用提示词
java·spring boot·提示词
kaico20182 小时前
JDK11新特性
java