(LeetCode-Hot100)56. 合并区间

合并区间(LeetCode 56)

🔗 问题简介

题目链接:https://leetcode.cn/problems/merge-intervals?envType=problem-list-v2&envId=2cktkvj

📝 题目描述

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


🧪 示例说明

示例 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. 合并逻辑
    • 初始化结果列表,将第一个区间加入。
    • 遍历后续每个区间:
      • 如果当前区间的起始位置 ≤ 结果中最后一个区间的结束位置 → 说明有重叠,合并(更新最后一个区间的结束位置为两者结束位置的最大值)。
      • 否则 → 没有重叠,直接将当前区间加入结果列表。

💡 为什么排序后只需比较相邻?

因为排序后,若区间 A 与 C 重叠但 A 与 B 不重叠、B 与 C 不重叠,则 A 的 end < B 的 start ≤ C 的 start,所以 A.end < C.start,不可能重叠。因此只需顺序合并即可。


🔁 其他解法(对比)

方法 思路 时间复杂度 空间复杂度 是否推荐
排序 + 贪心(主流) 如上所述 O(n log n) O(log n)(排序栈空间)或 O(n)(结果) ✅ 强烈推荐
插入排序式合并 每次插入新区间时尝试合并已有区间 O(n²) O(n) ❌ 效率低
扫描线算法 将所有端点标记为 start/end,扫描合并 O(n log n) O(n) ⚠️ 可行但复杂

📌 结论:排序 + 贪心是最简洁高效的方法。


💻 代码实现

java 复制代码
// Java 实现
import java.util.*;

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals.length == 0) return new int[0][2];
        
        // 按照起始位置排序
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
        
        List<int[]> merged = new ArrayList<>();
        merged.add(intervals[0]);
        
        for (int i = 1; i < intervals.length; i++) {
            int[] current = intervals[i];
            int[] last = merged.get(merged.size() - 1);
            
            if (current[0] <= last[1]) {
                // 重叠,合并
                last[1] = Math.max(last[1], current[1]);
            } else {
                // 不重叠,添加新区间
                merged.add(current);
            }
        }
        
        return merged.toArray(new int[merged.size()][]);
    }
}
go 复制代码
// Go 实现
import (
    "sort"
)

func merge(intervals [][]int) [][]int {
    if len(intervals) == 0 {
        return [][]int{}
    }

    // 按照起始位置排序
    sort.Slice(intervals, func(i, j int) bool {
        return intervals[i][0] < intervals[j][0]
    })

    merged := [][]int{intervals[0]}

    for i := 1; i < len(intervals); i++ {
        current := intervals[i]
        last := merged[len(merged)-1]

        if current[0] <= last[1] {
            // 重叠,合并
            last[1] = max(last[1], current[1])
        } else {
            // 不重叠,添加新区间
            merged = append(merged, current)
        }
    }

    return merged
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

🎯 示例演示

intervals = [[1,3],[2,6],[8,10],[15,18]] 为例:

  1. 排序后[[1,3],[2,6],[8,10],[15,18]](已有序)
  2. 初始化merged = [[1,3]]
  3. 处理 [2,6]
    • 2 ≤ 3 → 重叠 → 合并为 [1, max(3,6)] = [1,6]
    • merged = [[1,6]]
  4. 处理 [8,10]
    • 8 > 6 → 不重叠 → 添加
    • merged = [[1,6], [8,10]]
  5. 处理 [15,18]
    • 15 > 10 → 不重叠 → 添加
    • merged = [[1,6], [8,10], [15,18]]

✅ 最终结果正确。


✅ 答案有效性证明

我们需证明算法满足:

  1. 覆盖性 :输出区间覆盖所有输入区间。
    • 每个输入区间要么被合并进某个输出区间,要么自身成为输出区间。
  2. 无重叠性 :输出区间两两不重叠。
    • 因为只有当新区间起点 > 上一个区间终点时才添加,故任意两个相邻输出区间满足 prev.end < curr.start,自然无重叠。
  3. 最小性 :无法再合并。
    • 若存在可合并的两个输出区间,则它们在排序后应相邻,但算法已确保相邻区间不可合并。

因此,算法正确。


📊 复杂度分析

项目 分析
时间复杂度 O(n log n) ------ 主要消耗在排序;合并过程为 O(n)
空间复杂度 O(log n)(Java/Go 排序使用的栈空间) + O(n)(存储结果) 通常说 O(n)(因结果必须返回)

📌 注意:若原地修改允许,可优化空间,但本题要求返回新数组,故 O(n) 空间不可避免。


🧠 问题总结

  • 关键技巧排序 + 贪心合并 是处理区间类问题的经典范式。
  • 适用场景:所有"合并重叠区间"、"安排会议"、"最少箭引爆气球"等变种题。
  • 易错点
    • 忘记排序;
    • 合并时未取 max(end)(如 [1,4][2,3] 合并应为 [1,4],而非 [1,3]);
    • 边界条件(空输入)。

💡 延伸思考:若区间是动态插入的(如在线算法),可考虑使用平衡二叉搜索树维护区间,但本题为离线处理,排序最优。


github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
好学且牛逼的马1 小时前
从“XML汪洋”到“智能原生”:Spring Framework 1.x 到 7.x 演进全记录与核心知识点详解(超详细版)
java
wu_asia1 小时前
每日一练伍
算法
追随者永远是胜利者2 小时前
(LeetCode-Hot100)55. 跳跃游戏
java·算法·leetcode·游戏·go
近津薪荼2 小时前
优选算法——前缀和(7):连续数组
算法
知识即是力量ol2 小时前
Java 虚拟机:JVM篇
java·jvm·八股
快乐zbc2 小时前
苍穹外卖 - 菜品起售/停售复习笔记
java·笔记
ArturiaZ2 小时前
【day29】
数据结构·c++·算法
MoonOutCloudBack2 小时前
VeRL 框架下 RL 微调 DeepSeek-7B,比较 PPO / GRPO 脚本的参数差异
人工智能·深度学习·算法·语言模型·自然语言处理
Cosmoshhhyyy3 小时前
《Effective Java》解读第41条:用标记接口定义类型
java·开发语言