#57 区间合并 中等

插入区间

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给你一个无重叠的、按照区间起始端点排序的区间列表 intervals,其中 intervals[i] = [starti, endi] 表示第 i 个区间的开始和结束,并且 intervals 按照 starti 升序排列。同样给定一个区间 newInterval = [start, end] 表示另一个区间的开始和结束。

如果两个区间 至少 共享一个点,则认为它们是重叠的。

intervals 中插入区间 newInterval,使得 intervals 依然按照 starti 升序排列,且区间之间不重叠(如果有必要的话,可以合并区间)。

返回插入之后的 intervals

注意 你不需要原地修改 intervals。你可以创建一个新数组然后返回它。

示例 1

输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]
新区间 [2,5] 与 [1,3] 重叠,合并为 [1,5];[6,9] 在右侧无重叠,直接保留。

示例 2

输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:[[1,2],[3,10],[12,16]]
新区间 [4,8] 与 [3,5]、[6,7]、[8,10] 均重叠(端点 8 与 [8,10] 相接也算重叠),合并为 [3,10]。

💭 模拟答题者思考

1. 最直接:把 newInterval 插入 intervals 合适位置,再调用上一题「合并区间」的排序+扫描——可行但多了一次 O(n log n) 排序,而本题输入已经有序,浪费了结构信息。

2. 重复在哪里?合并区间需要排序是因为输入乱序;本题 intervals 已按起点升序且无重叠,插入只需一次线性扫描,按与 newInterval 的相对位置分三段处理。

3. 关键转化:三阶段扫描——(1) 所有终点 < newInterval[0] 的区间直接入 ans;(2) 所有与 newInterval 重叠的区间不断扩展 newInterval 的左右端点;(3) 将合并后的 newIntervalans,再把剩余区间依次 append。

4. 重叠判定:因 intervals 有序,阶段二条件为 intervals[i][0] <= newInterval[1](新区间右端点尚未被当前区间起点越过)。注意端点相接算重叠,与合并区间题一致。

5. 复杂度:每个区间访问一次,O(n) 时间、O(n) 输出空间——比「插入后重排+合并」更优。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
intervalslist<list<int>>定义:已按起点升序、两两不重叠的区间列表
维护:从左到右线性扫描,用指针 i 标记当前考察位置,不回溯
更新:每轮根据与 newInterval 的位置关系,将区间归入「左侧无重叠 / 待合并 / 右侧无重叠」三类之一
newIntervallist<int>定义:待插入的新区间 [start, end]
维护:在合并阶段不断扩展左右端点,直到与所有重叠区间融合成一块
更新newInterval[0] = min(newInterval[0], intervals[i][0])newInterval[1] = max(newInterval[1], intervals[i][1])
anslist<list<int>>定义:插入并合并后的结果区间列表
维护:始终按起点递增,且相邻区间互不相交
更新:阶段一 append 左侧区间;阶段二结束后 append 合并后的 newInterval;阶段三 append 剩余右侧区间
iint定义:扫描 intervals 的下标指针
维护:单调递增,每个区间最多访问一次
更新:每处理完一个区间 i += 1;三阶段共用同一指针,自然衔接

⌨️ 落码步骤

1. 初始化 ans = []i = 0n = len(intervals)

2. 阶段一:当 i < nintervals[i][1] < newInterval[0],将 intervals[i] 加入 ansi += 1

3. 阶段二:当 i < nintervals[i][0] <= newInterval[1],合并:newInterval[0] = min(...)newInterval[1] = max(...)i += 1

4. 将合并后的 newInterval 加入 ans

5. 阶段三:将 intervals[i:] 剩余区间依次加入 ans

6. 返回 ans(若 intervals 为空,阶段一、二跳过,直接返回 [newInterval]

💻 代码实现

class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        ans = []
        i, n = 0, len(intervals)
        # 阶段一:完全在 newInterval 左侧的区间
        while i < n and intervals[i][1] < newInterval[0]:
            ans.append(intervals[i])
            i += 1
        # 阶段二:与 newInterval 重叠,不断扩展
        while i < n and intervals[i][0] <= newInterval[1]:
            newInterval[0] = min(newInterval[0], intervals[i][0])
            newInterval[1] = max(newInterval[1], intervals[i][1])
            i += 1
        ans.append(newInterval)
        # 阶段三:剩余右侧区间
        while i < n:
            ans.append(intervals[i])
            i += 1
        return ans
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        vector<vector<int>> ans;
        int i = 0, n = intervals.size();
        // 阶段一:完全在 newInterval 左侧
        while (i < n && intervals[i][1] < newInterval[0]) {
            ans.push_back(intervals[i]);
            i++;
        }
        // 阶段二:重叠合并
        while (i < n && intervals[i][0] <= newInterval[1]) {
            newInterval[0] = min(newInterval[0], intervals[i][0]);
            newInterval[1] = max(newInterval[1], intervals[i][1]);
            i++;
        }
        ans.push_back(newInterval);
        // 阶段三:剩余右侧
        while (i < n) {
            ans.push_back(intervals[i]);
            i++;
        }
        return ans;
    }
};
// 时间 O(n),空间 O(n)(输出数组)

📈 复杂度分析

时间复杂度 O(n)
空间复杂度 O(n)(输出数组,不计输入)

⚠️ 常见坑

阶段一条件写成 <=:应为 intervals[i][1] < newInterval[0],若用 <= 会把端点相接的区间错误地留在阶段一而不合并(如 [1,3][3,5])。

阶段二条件写成 <:应为 intervals[i][0] <= newInterval[1],否则端点相接(如 newInterval=[4,8][8,10])无法合并。

忘记在阶段二结束后 append newInterval:合并后的新区间必须显式入 ans,否则结果缺少插入块。

🔍 必测边界 Case

Case 1:空列表
intervals = [], newInterval = [5,7] → [[5,7]](直接返回新区间)
Case 2:插入在最左侧
intervals = [[3,5],[12,15]], newInterval = [1,2] → [[1,2],[3,5],[12,15]](阶段一无元素,不合并,直接 append 后接原列表)
Case 3:插入在最右侧
intervals = [[1,2],[3,5]], newInterval = [6,8] → [[1,2],[3,5],[6,8]](阶段二无重叠,append 新区间后阶段三为空)
Case 4:与多个区间连续合并
intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] → [[1,2],[3,10],[12,16]](阶段二循环多次扩展)
Case 5:新区间完全包含已有区间
intervals = [[3,5]], newInterval = [1,10] → [[1,10]](合并后新区间吞掉原区间)