leetcode
leetcode 51 ~ 100
插入区间

插入区间

难度:

标签:

题目描述

给你一个 无重叠的按照区间起始端点排序的区间列表 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:

输入: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] 重叠。

 

提示:

  • 0 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 105
  • intervals 根据 starti升序 排列
  • newInterval.length == 2
  • 0 <= start <= end <= 105

代码结果

运行时间: 52 ms, 内存: 16.3 MB


/* 
* 思路:
* 1. 使用 Stream API 处理 intervals,将区间分为三部分:
*    - 完全在 newInterval 之前的区间
*    - 完全在 newInterval 之后的区间
*    - 与 newInterval 有重叠的区间
* 2. 合并重叠的区间并插入到结果中。
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
public class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        List<int[]> result = new ArrayList<>();
 
        // 添加所有在 newInterval 之前的区间
        Arrays.stream(intervals)
                .filter(interval -> interval[1] < newInterval[0])
                .forEach(result::add);
 
        // 合并重叠区间
        int[] mergedInterval = Arrays.stream(intervals)
                .filter(interval -> interval[0] <= newInterval[1] && interval[1] >= newInterval[0])
                .reduce(newInterval, (a, b) -> new int[]{Math.min(a[0], b[0]), Math.max(a[1], b[1])});
        result.add(mergedInterval);
 
        // 添加所有在 newInterval 之后的区间
        Arrays.stream(intervals)
                .filter(interval -> interval[0] > newInterval[1])
                .forEach(result::add);
 
        return result.toArray(new int[result.size()][]);
    }
}

解释

方法:

这个题解的思路是将新区间插入到原区间列表中的合适位置,同时合并所有重叠的区间。具体步骤如下: 1. 先将所有小于新区间左端点的区间添加到答案中 2. 然后将所有与新区间重叠的区间与新区间合并 3. 将合并后的新区间添加到答案中 4. 最后将剩余大于新区间右端点的区间添加到答案中

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在你的算法中,如何确保新插入区间后整个区间列表仍然保持排序?
在算法中,首先按照已排序的方式添加所有开始时间小于新区间的区间。然后处理可能与新区间重叠的区间,并与新区间合并。最后,添加所有剩余的、开始时间大于新区间的区间。这个过程从头到尾遵循原区间列表的顺序,因此可以确保整个列表在插入并合并后仍然是排序的。
🦆
你的解法中提到了合并重叠区间,能否详细解释什么情况下两个区间被视为重叠?
两个区间被视为重叠的情况是,一个区间的开始时间小于或等于另一个区间的结束时间。具体来说,如果有两个区间[a, b]和[c, d],当a <= d且b >= c时,这两个区间就重叠。这种重叠意味着它们至少有一个共同点或一个区间完全或部分地覆盖了另一个区间。
🦆
在合并重叠区间时,你选择了更新newInterval来代表合并后的区间。这样做有什么特别的优势吗?
选择更新newInterval作为合并后的区间的主要优势在于减少了额外的内存使用和简化了逻辑。通过直接更新newInterval的起始和结束点,我们避免了创建新的区间列表,同时使代码更加简洁。此外,这种方法也减少了不必要的数组操作,可能会在某些情况下提高效率。
🦆
如果新区间newInterval位于所有现有区间的最左端或最右端,你的算法处理流程会有什么不同吗?
如果newInterval位于所有现有区间的最左端或最右端,算法的处理流程具体如下:1. 如果newInterval在最左端,循环检查小于新区间左端点的区间将直接跳过,因为没有这样的区间;然后直接将newInterval添加到结果列表,并继续添加剩余的区间。2. 如果newInterval在最右端,所有现有的区间都会被添加到结果列表中,因为它们的结束时间都小于newInterval的开始时间;之后再添加newInterval。在这两种情况下,算法自然适应新区间的位置,无需特别的分支逻辑。

相关问题

合并区间

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

 

示例 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 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104

Range 模块

Range模块是跟踪数字范围的模块。设计一个数据结构来跟踪表示为 半开区间 的范围并查询它们。

半开区间 [left, right) 表示所有 left <= x < right 的实数 x

实现 RangeModule 类:

  • RangeModule() 初始化数据结构的对象。
  • void addRange(int left, int right) 添加 半开区间 [left, right),跟踪该区间中的每个实数。添加与当前跟踪的数字部分重叠的区间时,应当添加在区间 [left, right) 中尚未跟踪的任何数字到该区间中。
  • boolean queryRange(int left, int right) 只有在当前正在跟踪区间 [left, right) 中的每一个实数时,才返回 true ,否则返回 false
  • void removeRange(int left, int right) 停止跟踪 半开区间 [left, right) 中当前正在跟踪的每个实数。

 

示例 1:

输入
["RangeModule", "addRange", "removeRange", "queryRange", "queryRange", "queryRange"]
[[], [10, 20], [14, 16], [10, 14], [13, 15], [16, 17]]
输出
[null, null, null, true, false, true]

解释
RangeModule rangeModule = new RangeModule();
rangeModule.addRange(10, 20);
rangeModule.removeRange(14, 16);
rangeModule.queryRange(10, 14); 返回 true (区间 [10, 14) 中的每个数都正在被跟踪)
rangeModule.queryRange(13, 15); 返回 false(未跟踪区间 [13, 15) 中像 14, 14.03, 14.17 这样的数字)
rangeModule.queryRange(16, 17); 返回 true (尽管执行了删除操作,区间 [16, 17) 中的数字 16 仍然会被跟踪)

 

提示:

  • 1 <= left < right <= 109
  • 在单个测试用例中,对 addRange 、  queryRange 和 removeRange 的调用总数不超过 104 次