Day29 力扣贪心 : 860.柠檬水找零| 406.根据身高重建队列 |452. 用最少数量的箭引爆气球
今天的三道题目,都算是 重叠区间 问题,大家可以好好感受一下。 都属于那种看起来好复杂,但一看贪心解法,惊呼:这么巧妙!
还是属于那种,做过了也就会了,没做过就很难想出来。 不过大家把如下三题做了之后, 重叠区间 基本上差不多了
435. 无重叠区间
https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html
第一印象
和上一道题好像,但我不知道[1,2] [1,3]的排序结果是什么
看完题解的思路
也没有强调我的上面说的疑问。
首先排序,然后去找重叠区间的个数,其实和射箭是一样的,每一只箭就是一组重叠区间。只不过射箭是重叠的时候什么都不干,现在是重叠的时候计数。
我做重叠区间问题常有一个疑问
ab重叠,bc重叠,ac不重叠的情况。
其实这种就算ab重叠,ab整体和c不重叠。
射箭的话,一支箭不能射三个。
重叠区间的话,只要删除了b就没有重叠了,所以只是算一个。不然删除a,bc依然重叠,题目要求删最少。
实现中的困难
实现没困难
感悟
感受重叠区间问题吧
代码
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int result = 0;
//排序
Arrays.sort(intervals, (a,b)-> {
return Integer.compare(a[0],b[0]);
});
for (int i = 1; i < intervals.length; i++) {
//我的左边界如果小于上一个的右边界
if (intervals[i][0] < intervals[i - 1][1]) {
result++;
intervals[i][1] = Math.min(intervals[i][1], intervals[i - 1][1]);
}
}
return result;
}
}
763.划分字母区间
https://programmercarl.com/0763.%E5%88%92%E5%88%86%E5%AD%97%E6%AF%8D%E5%8C%BA%E9%97%B4.html
第一印象
每个字母都有开始出现和结束出现的位置。这个就相当于是区间,我们给这个区间排序。
如果两个区间有重叠,说明他们要分在一段,比如a [1,9] b [5,11],那么如果划分1~9,就不能保证b,因为5~9里有b,反之亦然。所以要划分1~11.
所以重叠区间就是一个片段,重叠之后视为整体,取大的那个右边界作为片段的右边界,再继续去找重叠……
我觉得思路是对的,但是就是怎么实现的问题了。
做出来了!!!!!!!!!!!!!!!!
看完题解的思路
写完实现困难回来看题解
题解有个非重叠区间的解法,不管了
实现中的困难
思路就是
获得每个字母的左右区间 -->
重叠的话就更新左右区间 -->
不重叠的话就记录上一个区间大小 (因为是上一个区间和我不重叠,那就是上一个区间已经结束了,就这么大) -->
最后记录最后一个区间的大小
我选择了数组作为哈希表,因为只有26个字母。那就涉及到一个问题,字符串不一定有26个不同字母啊,那排序的时候就一堆[0,0] 出来恶心人。
但也不能跳过所有[0,0] 去找第一个[0, k] 的,因为字符串可能是 abbbbb,a 就是[0,0] ,所以我初始化所有字母的下标为 MAX_VALUE。
获得每个字母区间的时候,遇到下标 MAX_VALUE 就说明第一次遇到这个字母,就要同时更新左右边界 (我在自己做的时候只更新了左边界,根据没过的用例改过来了)。如果遇到下标不是 MAX_VALUE 就说明第二次遇到这个字母,就只更新右边界。
再排序,我依然不知道Lambda表达式哈哈哈哈
再判断重叠的逻辑,不难这里。因为升序排列了,那些没出现的字母因为是MAX_VALUE,所以肯定都在最后面,如果遇到一个MAX_VALUE的,就break就行了。
//如果没有这个字母就break了,因为升序排列呀,没有的都是在最后面,只要有一个是这样的,就说明后面都是没有的。
if (hash[i][0] == Integer.MAX_VALUE) {
break;
}
同时也要记录一下有效字母到哪,因为要收集最后那个片段的长度,需要知道hash里有效字母到哪个位置。
手机最后片段的长度,就ok了。
自己做出来了舒服死了。
感悟
牛逼啊我!
代码
class Solution {
public List<Integer> partitionLabels(String s) {
int[][] hash = new int[26][26];
List<Integer> result = new ArrayList<>();
//初始化,每个字母的起始位置都是MAX_INTEGER
for (int i = 0; i < hash.length; i++) {
hash[i][0] = Integer.MAX_VALUE;
}
//获得每个字母的区间
for (int i = 0; i < s.length(); i++) {
char letter = s.charAt(i);
int index = letter - 'a';
//如果这个字母第一次遇到,更新左右边界
if (hash[index][0] == Integer.MAX_VALUE) {
hash[index][0] = i;
hash[index][1] = i;
} else {
//第二次遇到的话就只更新右边界
hash[index][1] = i;
}
}
//排序
Arrays.sort(hash, (a, b) -> Integer.compare(a[0], b[0]));
int lastIndex = 0;
for (int i = 1; i < hash.length; i++) {
//如果没有这个字母就break了,因为升序排列呀,没有的都是在最后面,只要有一个是这样的,就说明后面都是没有的。
if (hash[i][0] == Integer.MAX_VALUE) {
break;
}
//我的左边界比上一个的右边界大,我俩不重复
if (hash[i][0] > hash[i - 1][1]) {
int length = hash[i - 1][1] - hash[i - 1][0] + 1;
result.add(length);
//记录这个hash里有效字母到哪
lastIndex = i;
} else {
//我的左边界比上一个的右边界小,我俩重复了
//我俩的右边界得是最大的那个边界
hash[i][1] = Math.max(hash[i][1], hash[i - 1][1]);
//我俩的左边界得是最小的边界, 为了记录这个片段的起始位置
hash[i][0] = Math.min(hash[i][0], hash[i - 1][0]);
//记录这个hash里有效字母到哪
lastIndex = i;
}
}
//收获最后一个片段的长度
int lastLength = hash[lastIndex][1] - hash[lastIndex][0] + 1;
result.add(lastLength);
return result;
} }
56. 合并区间
本题相对来说就比较难了。
https://programmercarl.com/0056.%E5%90%88%E5%B9%B6%E5%8C%BA%E9%97%B4.html
第一印象
我和上一个重叠的话,同时更新我的左右边界。这个感觉还没我上一道做的难呢,试试吧。
答案是作对了,但是result 最后莫名其妙多出来一个[0,0]
我看看怎么回事
因为我result的声明是
int[][] result = new int[intervals.length][2];
初始化的时候就会多东西
应该用LinkedList
看完题解的思路
和我一样,无敌啦~!!!!!!!
实现中的困难
对LinkedList 泛型是数组的时候完全不会,还有LinkedList 变成二维数组的语法。。。。。
晕啦
//list里都是数组
LinkedList<int[]> result = new LinkedList<>();
//往list里加元素,每个元素都是数组,直接把左右边界初始化进去
result.add( new int[] {a, b} );
//list转成数组,第一个参数要是list的大小,这个我看不懂
result.toArray( new int [ result.size() ] [] );
上面的第三个的参数我看不懂意义。是因为result里面的东西都是第二个参数的,所以只需要指明变成二维数组的话,大小是多少就行了吗??
感悟
主要是java语言基础不过关,重复区间已经无敌
代码
class Solution {
public int[][] merge(int[][] intervals) {
LinkedList<int[]> result = new LinkedList<>();
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
for (int i = 1; i < intervals.length; i++) {
//我的左边界小于等于上一个的右边界,我俩重叠, 我俩合并
if (intervals[i][0] <= intervals[i - 1][1]) {
//重叠就更新我的左右边界,左边的选最小的,右边的选最大的
intervals[i][0] = intervals[i - 1][0];
intervals[i][1] = Math.max(intervals[i][1], intervals[i - 1][1]);
} else {
//我和上一个不重叠的话
//记录上一个的左右边界,就是一个区间的答案。
result.add(new int[] {intervals[i - 1][0], intervals[i - 1][1]});
}
}
//最后收集最后一个区间的答案
result.add(new int[] {intervals[intervals.length - 1][0], intervals[intervals.length - 1][1]});
return result.toArray(new int[result.size()][]);
}
}