题目描述:
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).
You may assume that the intervals were initially sorted according to their start times.
Example 1:
Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]
Example 2:
Input: intervals =[[1,2],[3,5],[6,7],[8,10],[12,16]]
, newInterval =[4,8]
Output: [[1,2],[3,10],[12,16]] Explanation: Because the new interval[4,8]
overlaps with[3,5],[6,7],[8,10]
.
NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature.
思路一:(开辟新空间后合并)
直观的想法是开辟一个新空间,将newInterval和intervals中的元素放到新数组中。插入的逻辑是newInterval的start和原来Intervals的start进行比较,若比前一个小但是比后一个大,则插入到两者中间,再对这个新的二维数组按照leetcode56 题的方法进行合并。
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
if(intervals==null||intervals.length==0){
return new int[][]{newInterval};//返回一个二维数组
}
int[][] newArr=new int[intervals.length+1][2];
int i;
for(i=0;i<newArr.length;i++){
//在到达最后一个元素之前还没break,说明旧数组中的interval的start都比新插入的小,所以还没有break
//此时直接将新插入的interval放到newArr的最后一位即可
if(i==newArr.length-1){
newArr[i]=newInterval;
break;
}
if(intervals[i][0]<newInterval[0]){
newArr[i]=intervals[i];
}else{
newArr[i]=newInterval;
break;
}
}
int j=i+1;
while(j<newArr.length){
newArr[j++]=intervals[i++];
}
return merge(newArr);
}
public int[][] merge(int[][] intervals) {
int len=intervals.length;
int num=0;//记录合并完数组的元素个数
for(int i=1;i<len;i++){
if(intervals[i][0]<=intervals[num][1]){//第二个区间的start小于第一个区间的end
if(intervals[i][1]>intervals[num][1]){
intervals[num][1]=intervals[i][1];
}
}else{//不能合并,将数字加入
num++;
intervals[num]=intervals[i];//将当前的interval设置成最新的inerval
}
}
return Arrays.copyOfRange(intervals,0,num+1);// copyOfRange将指定数组的指定范围复制到一个新数组。
}
}
思路二、(一边判断一边合并)
从头开始遍历intervals,在遍历的过程中逐个判断newInterval和intervals的当前元素是否可以合并(有交集就可以合并),合并后更新newIntervals的值(更新为合并后的值),再接着用这个newIntervals和下一个元素判断,是否可以合并。
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
if(intervals==null||intervals.length==0){
return new int[][]{newInterval};//返回一个二维数组
}
ArrayList<int[]> list=new ArrayList<>();
for(int i=0;i<intervals.length;i++){
if(newInterval[0]<intervals[i][0]){
int[] tmp=intervals[i];
intervals[i]=newInterval;
newInterval=tmp;
}
//判断是否可以合并,若不可,则将当前intervals加入ret,继续和下一个做判断
//若可以,则进行合并,将合并后的结果和下一个做判断
if(isInsect(intervals[i], newInterval)){
//进行合并,并更新interval
newInterval[0]=Math.min(intervals[i][0],newInterval[0]);
newInterval[1]=Math.max(intervals[i][1],newInterval[1]);
}else{
//判断当前需要进行合并的是否是最后一个区间
if(i==intervals.length-1){
list.add(intervals[i]);
list.add(newInterval);
}else {
list.add(intervals[i]);//将当前intervals加入ret,继续用这个newInterval和下一个区间判断
}
}
}
//防止合并后成为最后一个区间的可能性
if(!list.contains(newInterval)){
list.add(newInterval);
}
int[][] ret=new int[list.size()][2];
int k=0;
for(int[] ele:list){
ret[k++]=ele;
}
return ret;
}
private boolean isInsect(int[] o1,int[] o2){
if(o2[0]<=o1[1]){//02的start小于o1的end
return true;
}
return false;
}
}
思路三、
不排序,直接插入时间间隔,插入的时间间隔的位置分为三部分:
1.插入位置的左侧
2.插入位置(有重叠/无重叠)
3.插入位置的右侧
这三个位置分别做处理,只在插入位置处理可能的情况即可,时间复杂度o(n),空间复杂度o(n)
实现3:参考了这篇博客的思路https://blog.csdn.net/makuiyu/article/details/44265105
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
if(intervals==null||intervals.length==0){
return new int[][]{newInterval};//返回一个二维数组
}
int[][] newArr=new int[intervals.length+1][2];
int i=0,j=0;
//先插入比newInterval小的前半截
while(i<intervals.length&&intervals[i][1]<newInterval[0]){
newArr[j++]=intervals[i++];
}
//再插入newInterval
newArr[j]=newInterval;
while(i<intervals.length&&newArr[j][1]>=intervals[i][0]){
newArr[j][0]=Math.min(intervals[i][0],newInterval[0]);
newArr[j][1]=Math.max(intervals[i][1],newInterval[1]);
i++;
}
while (i<intervals.length){
newArr[++j]=intervals[i++];
}
return Arrays.copyOfRange(newArr,0,j+1);
}
}
实现4:推荐使用方法
和实现2的思路是一样的,就是一边遍历intervals,一边进行合并;
因为我们在判断两个interval是否有交集时,必须先对这两个interval按照start进行排序,再将排序后后一个interval的start和前一个interval的end进行比较来判断。
进行此时再合并的过程中会遇到两种情况,
1. intervals[i]的start比newInterval的start小,此时intervals[i]排在前面
此时如果intervals[i]的end比newInterval的start小,则两者必定没有交集,此时我们将intervals[i]加入结果集。
2.intervals[i]的start比newInterval的start大,此时newInterval需要排在前面
此时如果newInterval的intervals的start小,则两者必定没有交集,此时我们将intervals[i]加入结果集。
如果不是这两种没有交集的情况,则intervals[i]和newIntervalyi一定有交集,此时我们通过求交集的方法更新newInterval.
然后接着用这个newInterval和intervals[i]进行判断。
求交集:起点取两个interval start的较小者
终点取两个interval end的较大者
public int[][] insert3(int[][] intervals, int[] newInterval) {
if (intervals == null || newInterval == null) {
return intervals;
}
List<int[]> res = new ArrayList<>();
int p = 0;//记录上一次插入左边interval的位置
for (int[] item : intervals) {
//item的start比newInterval的start小,此时通过判断item的end和newInterval的start来确定是否有交集。没有交集,则将当前item加入结果集
if (item[1] < newInterval[0]) {
res.add(item);
++p;
} else if (newInterval[1]<item[0]) {
//item的start比newInterval的start大,此时通过判断item的start和newInterval的end来确定是否有交集
res.add(item);
} else {//有交集,则获取交集
newInterval[0] = Math.min(item[0], newInterval[0]);
newInterval[1] = Math.max(item[1], newInterval[1]);
}
}
res.add(p, newInterval);
int[][] ans = new int[res.size()][2];
for (int i = 0; i < res.size(); ++i) {
ans[i] = res.get(i);
}
return ans;
}