题目
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 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] 重叠。示例 3:
输入:intervals = [], newInterval = [5,7]
输出:[[5,7]]示例 4:
输入:intervals = [[1,5]], newInterval = [2,3]
输出:[[1,5]]示例 5:
输入:intervals = [[1,5]], newInterval = [2,7]
输出:[[1,7]]提示:
0 <= intervals.length <= 10^4
intervals[i].length == 2
0 <= intervals[i][0] <= intervals[i][1] <= 10^5
intervals 根据 intervals[i][0] 按 升序 排列
newInterval.length == 2
0 <= newInterval[0] <= newInterval[1] <= 10^5来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/insert-interval
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
做过了「2276. 统计区间中的整数数目」回来复习,顺着那道题的思路就有了下面的代码一,先二分查找到最后一个可能重叠的区间,然后往前找到第一个重叠的区间,将这个范围之内的区间全部合并,改为新的合并后的区间,返回结果。
代码
代码一
class Solution {public int[][] insert(int[][] intervals, int[] newInterval) {int n = intervals.length;// 这里必须对空数组特判,否则二分的后判会下标溢出if (n == 0) {return new int[][] {newInterval};}// end为最后一个「可能」和newInterval重叠的区间的下标// 注意是可能,因为可能没有,不过即使没有,下面的逻辑也可以统一,因此即使end为-1也不用单独讨论// 二分找到最后一个不大于newInterval[1]的区间的右端点,返回下标int end = bsearch(intervals, newInterval[1]);int left = newInterval[0];int right = newInterval[1];// 找到第一个和newInterval重叠的区间,因为最后还会--,所以start+1才是第一个重叠的区间int start = end;while (start >= 0 && intervals[start][1] >= left) {left = Math.min(left, intervals[start][0]);right = Math.max(right, intervals[start][1]);start--;}List<int[]> ans = new ArrayList<>();// [0,start]和[end+1,n-1]这些区间是和newInterval不重叠的for (int i = 0; i <= start; i++) {ans.add(intervals[i]);}// 在这里加入合并后的新区间ans.add(new int[] {left, right});for (int i = end + 1; i < n; i++) {ans.add(intervals[i]);}return ans.toArray(new int[0][]);}private int bsearch(int[][] intervals, int num) {int l = 0;int r = intervals.length - 1;while (l < r) {int mid = l + (r - l + 1) / 2;if (intervals[mid][0] > num) {r = mid - 1;} else {l = mid;}}if (intervals[l][0] <= num) {return l;}return -1;}}
代码二(推荐)
上面的方法即使用了二分,最坏时间仍然是O(n),所以不如直接遍历,不使用二分,代码简洁一些
class Solution {public int[][] insert(int[][] intervals, int[] newInterval) {int n = intervals.length;List<int[]> ans = new ArrayList<>();int left = newInterval[0];int right = newInterval[1];int i = 0;// intervals[i][0] <= newInterval[1]表示遍历到最后一个可能重叠的区间就停止for (; i < n && intervals[i][0] <= newInterval[1]; i++) {// 将newInterval左边的和其不重叠的区间直接添加到结果中if (intervals[i][1] < newInterval[0]) {ans.add(intervals[i]);} else {// 对于重叠的区间,计算新区间的左右端点left = Math.min(left, intervals[i][0]);right = Math.max(right, intervals[i][1]);}}// 加入合并后的区间ans.add(new int[]{left, right});// 再加入newInterval右侧不重叠的区间for (int j = i; j < n; j++) {ans.add(intervals[j]);}return ans.toArray(new int[0][]);}}
