目录
今天的三道题目,都算是 重叠区间 问题,大家可以好好感受一下。 都属于那种看起来好复杂,但一看贪心解法,惊呼:这么巧妙! 还是属于那种,做过了也就会了,没做过就很难想出来。不过大家把如下三题做了之后, 重叠区间 基本上差不多了
435. 无重叠区间
题解思路:
本题和射爆气球的最小弓箭数量的思路一致,需要先把数组按从小到大排列,然后再判断,需要注意以下两点:
1、只有当前一个数组元素的右边界大于当前数组元素的左边界,说明两个数组一定重叠,此时才对结果result做++操作
2、还需要判断与下面的区间是否重叠,因此需要重新更新下数组元素的右边界,为了使得移除区间的数量最小,因此需要更新为当前重叠区间右边界的最小值!!!这点很重要也很容易忽略
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals, (a,b)-> {
if(a[0] == b[0]) Integer.compare(a[1], b[1]);
return Integer.compare(a[0], b[0]);
}); //如果元素数组的第一个元素相同,按照第二个元素从小到大排列;如果不相同,则按照第一个元素的值从小到大排列
int result = 0;
for(int i = 1; i < intervals.length; i++){
if(intervals[i - 1][1] > intervals[i][0]){ //只有当前一个数组元素的右边界大于当前数组元素的左边界,说明两个数组一定重叠,但是还需要判断与下面的区间是否重叠,因此需要重新更新下数组元素的右边界,为了使得移除区间的数量最小,因此需要更新为当前重叠区间右边界的最小值!!!这点很重要也很容易忽略
result++;
intervals[i][1] = Math.min(intervals[i-1][1],intervals[i][1]);
}
}
return result;
}
}
763.划分字母区间
题解思路:
本题主要分两步走:
第一步:记录每个元素出现的最远位置,由于后面出现相同字母的下标值会不断的覆盖前面记录的下标值,因此遍历结束后,对应字母处的元素值记录的就是该字母出现的最远位置;
第二步:第二次遍历整个字符串,获取切割字符串片段的长度时,需要不断更新右边界,直到遍历片段中同一个字母出现的最大位置,一旦遍历到该最大位置就理解记录,从而保证可以尽可能的划分多个片段。
class Solution {
public List<Integer> partitionLabels(String s) {
int[] hashSet = new int[26];
for(int i = 0; i < s.length(); i ++){
hashSet[s.charAt(i) - 'a'] = i; //第一步:记录每个元素出现的最远位置,由于后面出现相同字母的下标值会不断的覆盖前面记录的下标值,因此遍历结束后,对应字母处的元素值记录的就是该字母出现的最远位置
}
List<Integer> result = new ArrayList<>(); //存放结果数组
int left = 0, right = 0; //使用双指针,记录切割字符串区间片段的起始和终止位置
for(int i = 0; i < s.length(); i++){
right = Math.max(hashSet[s.charAt(i) - 'a'], right);//不断更新右边界,直到遍历片段中同一个字母出现的最大位置,一旦遍历到该最大位置就理解记录,从而保证可以尽可能的划分多个片段
if(i == right){
result.add(right-left+1); //记录对应片段的字符串个数
left = i + 1;
}
}
return result;
}
}
56. 合并区间
本题相对来说就比较难了。
题解思路:
和之前的重叠区间处理的逻辑一致,唯一写卡住的就是如何把第一个数组元素存入到结果集中,这里比较巧妙的处理方式是直接把第一个数组元素存入到结果集中,处理重叠区间的时候一般都是从第二个数组元素开始遍历,那么这里是直接取出结果集中最后一个数组元素,然后直接修改结果集中最后一个数组元素,进行合并后的区间(如果可以合并的话),其实也就等价于上一个数组的右边界和当前数组的左边界进行比较,如果上一个数组右边界大于等于当前数组的左边界,因此(如果可以合并的话)也省略了再次重复添加的过程!!!
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a,b) -> {
if(a[0] == b[0]) return Integer.compare(a[1], b[1]);
return Integer.compare(a[0],b[0]);
});
LinkedList<int[]> result = new LinkedList<>();
result.addLast(intervals[0]); //结果集中至少有个一个数组元素,就是数组中的第一个数组元素
//直接取出结果集中最后一个数组元素,然后修改该数组合并后的区间(如果可以合并的话),其实也就等价于上一个数组的右边界和当前数组的左边界进行比较,如果上一个数组右边界大于等于当前数组的左边界,那么进行下面的区间合并操作
for(int i = 1; i < intervals.length; i++){
if(result.getLast()[1] >= intervals[i][0]){
intervals[i][0] = Math.min(result.getLast()[0], intervals[i][0]); //这行没必要使用min()函数,因为数组元素就是按照左边界进行从小到大排序,所有存入到结果集中的数组如果两个区间可以合并,那么左边界一定是这两个区间左边界的最小值
intervals[i][1] = Math.max(result.getLast()[1], intervals[i][1]); //由于数组是按照按照左边界进行从小到大排序,因为对于两个区间的右边界是不确定谁大谁小的,所有要使用max()函数进行比较取值,更新最大的右边界即可
result.removeLast();
result.addLast(new int[]{intervals[i][0], intervals[i][1]});
}else{
result.add(intervals[i]);
}
}
return result.toArray(new int[result.size()][]);
}
}