Nameless Site

But one day, you will stand before its decrepit gate,without really knowing why.

0%

插入区间

来源Leetcode第57题插入区间

给出一个无重叠的 ,按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:

输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]


排序

同合并区间,就是要注意当intervals为空时,可以直接返回newInterval

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int []> ans = new ArrayList<>();
if(intervals == null || intervals.length == 0)
{
ans.add(newInterval);
return ans.toArray(new int[0][]);
}

int [][] matrix = new int[intervals.length + 1][2] ;
System.arraycopy(intervals,0,matrix,0,intervals.length);
matrix[intervals.length] = newInterval;
//根据二维数组第一个数字大小按每一行整体排序
Arrays.sort(matrix, new Comparator<int[]>() {
@Override
public int compare(int[] ints, int[] t1) {
return ints[0] - t1[0] ;
}
});
int i = 0 ;
while(i < matrix.length){
int left = matrix[i][0];
int right = matrix[i][1];
//合并区间,判断right 是否比下一个区间的左值大
while(i < matrix.length && right>= matrix[i][0]){
//重新对right赋值
right = Math.max(right,matrix[i][1]);
i++;
}
ans.add(new int[] {left,right});
}
return ans.toArray(new int [0][]);
}

插入后合并

注意到初始数组是无重叠的 ,按照区间起始端点排序的,因而可以先找到要插入的地方,然后前后合并即可。
具体思路见注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Solution {

//首先通过二分查找找到要插入的位置
public int midSearch(int[][] intervals, int[] newInterval){
int start = 0;
int end = intervals.length;
int mid;
while (start<end){
mid = (start+end)/2;
if(intervals[mid][0]<newInterval[0])
start = mid+1;
else
end = mid;
}
return start;
}

public int[][] insert(int[][] intervals, int[] newInterval) {
int count = 0; //计数,计算合并多少了个区间
int[] buffer = new int[2];
buffer[0] = newInterval[0];
buffer[1] = newInterval[1];
int place = midSearch(intervals,newInterval);//找到插入位置

//向后合并
int temp;
for(temp = place;temp<intervals.length;temp++){
if(buffer[1]<intervals[temp][0])
break;
}
//这时候temp如果不和place相等,说明插入的区间的右值比后续若干区间的左值都大
//这时候要更新新的区间右值
if(temp!=place){
buffer[1] = Math.max(intervals[temp-1][1],buffer[1]);
count += temp - place;
}
//向前合并
if (place!=0){
//如果插入点前一位置的右值比插入区间的左值大
if(intervals[place-1][1]>=buffer[0]){
//那么更新合并区间的左值和右值
buffer[0] = intervals[place-1][0];
buffer[1] = Math.max(intervals[place-1][1],buffer[1]);
//同时插入点倒退,合并数+1;
place--;
count++;
}
}
//储存结果
int target;
int[][] result = new int[intervals.length - count + 1][2];
for(target = 0;target<place;target++){
result[target][0] = intervals[target][0];
result[target][1] = intervals[target][1];
}
result[target][0] = buffer[0];
result[target][1] = buffer[1];
target++;
for(;temp<intervals.length;temp++,target++){
result[target][0] = intervals[temp][0];
result[target][1] = intervals[temp][1];
}
return result;
}
}

常规思考

来自题解

先考虑三种极端情况:

  • intervals为空
  • newInterval[1] < intervals[0][0],直接插入第一个位置
  • newInterval[0] > intervals[-1][1],直接插入最后一个位置

下面就要考虑重叠情况了

我们目标就是找到和newInterval相关那几个区间.

首先,左边,当newInterval[0] > intervals[i][1]说明没有和该区间没有重叠部分,继续遍历下一个区间,比如intervals = [[1,3],[6,9]], newInterval = [2,5]

然后,再看右边,这里有个情况,就是 当intervals[i][0] > newInterval[1]说明newInterval没有和任何区间重合,比如intervals = [[1,3],[6,9]], newInterval = [4,5],直接插入即可.

接下来我们要找右边重合区域,当while i < n and newInterval[1] >= intervals[i][0]说明有重叠部分,记录左边最大值!

最后把数组拼接一下即可!

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> res = new ArrayList<>();
int i = 0;
while (i < intervals.length && newInterval[0] > intervals[i][1]) {
res.add(intervals[i]);
i++;
}
int[] tmp = new int[]{newInterval[0], newInterval[1]};
//合并右边
while (i < intervals.length && newInterval[1] >= intervals[i][0]) {
tmp[0] = Math.min(tmp[0], intervals[i][0]);
tmp[1] = Math.max(tmp[1], intervals[i][1]);
i++;
}
res.add(tmp);
//添加剩余部分
while (i < intervals.length) {
res.add(intervals[i]);
i++;
}
return res.toArray(new int[0][]);
}
}