前言
保持有序数组对二分法的紧密联系。问题分析最好用纸笔画画,弥补空间思维和记忆量小的不足。
一、插入区间
二、二分+区间确定
1、拆解问题、分类讨论
/*
target:给一个有序且不重叠的区间数组,把一个新区间合并进去。
有序数组紧密关联这二分查找。
合并区间需要考虑合并到那个位置?需要进行二分查找,找起点,找终点。
找到的起点和终点对应interval的数组要和newInterval来比较,看是否需要合并区间。
M1:找到插入数组左端,通过interval[left][1] >= newInterval[0]来判断是否要合并左端前一个数组,把前面的区间加到list,并确定新区间左端点。
找到插入数组右端,通过interval[right][0] <= newInterval[1]来判断是否要合并right数组,确定新区间右端点。
添加新区间,再添加右边的剩余区间。
关键点:确定左边右边不需要合并的区间有多长;确定插入新区间的左右端点(因为可能涉及到合并区间,就要重设端点)。
总结:二分法O(logN) + 是否合并区间,来确定两个东西。1-左右剩余区间;2-中间的新区间。
*/
2、code
package com.xhu.offer.everyday;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
//插入区间
public class Insert {
/*
target:给一个有序且不重叠的区间数组,把一个新区间合并进去。
有序数组紧密关联这二分查找。
合并区间需要考虑合并到那个位置?需要进行二分查找,找起点,找终点。
找到的起点和终点对应interval的数组要和newInterval来比较,看是否需要合并区间。
M1:找到插入数组左端,通过interval[left][1] >= newInterval[0]来判断是否要合并左端前一个数组,把前面的区间加到list,并确定新区间左端点。
找到插入数组右端,通过interval[right][0] <= newInterval[1]来判断是否要合并right数组,确定新区间右端点。
添加新区间,再添加右边的剩余区间。
关键点:确定左边右边不需要合并的区间有多长;确定插入新区间的左右端点(因为可能涉及到合并区间,就要重设端点)。
总结:二分法O(logN) + 是否合并区间,来确定两个东西。1-左右剩余区间;2-中间的新区间。
*/
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> rs = new ArrayList<>();
int left = binarySearch(intervals, newInterval[0], 0);
if (left != 0) {
if (intervals[left - 1][1] >= newInterval[0]) {
--left;
newInterval[0] = intervals[left][0];
}
}
for (int i = 0; i < left; i++) rs.add(intervals[i]);
int right = binarySearch(intervals, newInterval[1], 1);
if (right != intervals.length && newInterval[1] >= intervals[right][0]) {
newInterval[1] = intervals[right][1];
++right;
}
rs.add(newInterval);
for (int i = right; i < intervals.length; i++) rs.add(intervals[i]);
return rs.toArray(new int[0][]);
}
/**
* 插入式,插左
*
* @param intervals
* @param target
* @param mark mark == 0 表示按intervals元素中的左端点来找,反之按右端点来找。
* @return
*/
private int binarySearch(int[][] intervals, int target, int mark) {
int low = 0, high = intervals.length;
while (low < high) {
int mid = low + (high - low >>> 1);
int midVal = mark == 0 ? intervals[mid][0] : intervals[mid][1];
if (target > midVal) low = mid + 1;
else high = mid;
}
return high;
}
}
总结
1)二分法与有序数组(或者把数组先排个序)
2)纸笔可减少头脑记忆的负担,专注思考和分析问题,方便情况分类和整理。
3)问题拆解,牢牢抓住关键点。
参考文献
[1] LeetCode 插入区间