Interval是一类型的题目,面试的很喜欢出,这里把所有interval的题目全部总结一下;
Merge Intervals 首先按照start sort之后,判断end是否跟start相交,如果相交,end就是两者最大值;否则加入cur;注意最后需要加入cur;
class Solution {
public int[][] merge(int[][] intervals) {
if(intervals == null || intervals.length == 0) {
return new int[0][0];
}
// sort based on start;
Arrays.sort(intervals, (a, b) -> (a[0] - b[0]));
List<int[]> list = new ArrayList<int[]>();
int[] last = new int[]{intervals[0][0], intervals[0][1]};
for(int i = 1; i < intervals.length; i++) {
int[] cur = new int[]{intervals[i][0], intervals[i][1]};
if(last[1] < cur[0]) {
list.add(last);
last = cur;
} else {
last[1] = Math.max(last[1], cur[1]);
}
}
list.add(last); // 注意最后需要加入last;
int[][] result = new int[list.size()][2];
int index = 0;
for(int[] interval: list) {
result[index][0] = interval[0];
result[index][1] = interval[1];
index++;
}
return result;
}
}
Insert Interval 思路:分三种情况,一种是interval 在newInterval前面, 一种是newInterval在interval前面,一种是相交;
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
if(intervals == null || intervals.length == 0) {
int[][] res = new int[1][2];
res[0][0] = newInterval[0];
res[0][1] = newInterval[1];
return res;
}
List<int[]> list = new ArrayList<int[]>();
for(int[] interval: intervals) {
if(interval[1] < newInterval[0]) {
// interval, newInterval;
list.add(interval);
} else if(newInterval[1] < interval[0]) {
// newInterval, interval;
list.add(newInterval);
// 不要忘记newInterval往后挪动;
newInterval = interval;
} else {
newInterval[0] = Math.min(newInterval[0], interval[0]);
newInterval[1] = Math.max(newInterval[1], interval[1]);
}
}
// 不要忘记加newInterval;
list.add(newInterval);
int[][] res = new int[list.size()][2];
for(int i = 0; i < list.size(); i++) {
int[] temp = list.get(i);
res[i][0] = temp[0];
res[i][1] = temp[1];
}
return res;
}
}
Interval List Intersections 跟Time Intersection一样,因为是有序的,可以直接打擂台的方式,扫描,start取最大值,end取最小值,如果start <= end就加入 res。否则,淘汰掉end在前面的那个interval;
class Solution {
public int[][] intervalIntersection(int[][] A, int[][] B) {
List<int[]> list = new ArrayList<int[]>();
int a = 0; int b = 0;
while(a < A.length && b < B.length) {
// { A[a][0], A[a][1] };
// { B[b][0], B[b][1] };
int start = Math.max(A[a][0], B[b][0]);
int end = Math.min(A[a][1], B[b][1]);
if(start <= end) {
list.add(new int[] {start, end});
}
// 无论相交不相交,都要从a, b中间踢走一个;
// start >= end;
if(A[a][1] > B[b][1]) {
b++;
} else {
a++;
}
}
int[][] res = new int[list.size()][2];
int index = 0;
for(int[] item: list) {
res[index][0] = item[0];
res[index][1] = item[1];
index++;
}
return res;
}
}
Remove Interval 就是分情况讨论,cur在delete的哪个部位,相应做处理;非常适合电面,这题;
class Solution {
public List<List<Integer>> removeInterval(int[][] intervals, int[] toBeRemoved) {
List<List<Integer>> lists = new ArrayList<List<Integer>>();
if(intervals == null || intervals.length == 0) {
return lists;
}
int removeStart = toBeRemoved[0];
int removeEnd = toBeRemoved[1];
for(int[] interval : intervals) {
int start = interval[0];
int end = interval[1];
// no overlap;
if(end <= removeStart || removeEnd <= start) {
lists.add(Arrays.asList(start, end));
}
// 前面overlap
if(start < removeStart && removeStart < end) {
lists.add(Arrays.asList(start, removeStart));
}
// 完全在delete中间;
if(removeStart < start && end < removeEnd) {
continue;
}
// 后面overlap;
if(start < removeEnd && removeEnd < end) {
lists.add(Arrays.asList(removeEnd, end));
}
}
return lists;
}
}
Remove Covered Intervals 核心思想就是:算overlap了多少个,然后用总个数减去overlap的个数。如何计算overlap就是:sort array , 首先按照x sort,如果x相同,y按照从大到小sort,这样只有前面的能够cover后面的,后面的不能cover前面的,因为x都排序了,y是按照从大到小排序的。
class Solution {
public int removeCoveredIntervals(int[][] intervals) {
if(intervals == null || intervals.length == 0) {
return 0;
}
int[] last = null;
Arrays.sort(intervals, (a, b) -> (a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]));
int count = 0;
for(int[] interval : intervals) {
if(last == null) {
last = interval;
} else {
if(last[1] >= interval[1]) {
count++;
} else {
last = interval;
}
}
}
return intervals.length - count;
}
}
Non-overlapping Intervals 按照start,排序,判断是否有重复也就是end是否大于后面的start,如果大于,就有重复,重复的话,要去掉的就是end最大的那个,也就是保留下end比较小的,这样去掉end最大的,也就是保证了remove最少的;
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals == null || intervals.length == 0) {
return 0;
}
Arrays.sort(intervals, (a, b) -> (a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]));
int[] cur = null;
int count = 0;
for(int[] interval : intervals) {
if(cur == null) {
cur = interval;
} else {
if(cur[1] > interval[0]) {
count++;
cur[1] = Math.min(cur[1], interval[1]);
} else {
// cur[1] <= interval[0];
cur = interval;
}
}
}
return count;
}
}
Find Right Interval 思路:题目要求很明显就是找第一个比自己大的start >= 我当前的end。所以找第一个比自己大,这个东西很好用treemap, 每次查找是logn, ceilingKey来找第一个比自己大的interval start,treemap里面存start和index;
class Solution {
public int[] findRightInterval(int[][] intervals) {
if(intervals == null || intervals.length == 0) {
return new int[0];
}
int n = intervals.length;
TreeMap<Integer, Integer> treemap = new TreeMap<>();
for(int i = 0; i < n; i++) {
treemap.put(intervals[i][0], i);
}
int[] res = new int[n];
for(int i = 0; i < n; i++) {
Integer ceilingKey = treemap.ceilingKey(intervals[i][1]);
if(ceilingKey == null) {
res[i] = -1;
} else {
res[i] = treemap.get(ceilingKey);
}
}
return res;
}
}