所用代码 java
无重叠区间 LeetCode 435
题目链接:无重叠区间 LeetCode 435 - 中等
思路
本题和昨天射气球的题一模一样。
贪心贪的每次保留最小的右区间,才可以留下最多的区间。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
// 需要移除区间的数量
int count = 0;
// 按左边界排序
Arrays.sort(intervals, (o1,o2) -> o1[0] - o2[0]);
// 从第二个区间开始比较
for (int i = 1; i < intervals.length; i++) {
// 后一个区间的左边界大于等于前一个区间的有边界,就没重叠
if (intervals[i][0] >= intervals[i-1][1]){
continue;
}else {
count++;
// 有重叠的时候,把最小的右边界保留
intervals[i][1] = Math.min(intervals[i][1], intervals[i-1][1]);
}
}
return count;
}
}
总结
我们的核心在于intervals[i][1] = Math.min(intervals[i][1], intervals[i-1][1]);
举例:[1, 10], [2, 3], [4, 5], [6, 7] 这四个区间只用移除 [1,10] 其他的就都不重复了!!
本题还可以按右边界排序,代码也是类似的:
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
// 需要移除区间的数量
int count = 0;
// 按右边界排序
Arrays.sort(intervals, (o1,o2) -> o1[1] - o2[1]);
// 从第二个区间开始比较
for (int i = 1; i < intervals.length; i++) {
// 后一个区间的左边界大于等于前一个区间的右边界,就没重叠
if (intervals[i][0] >= intervals[i-1][1]){
continue;
}else {
// 贪心:更新最小的右区间
intervals[i][1] = Math.min(intervals[i][1],intervals[i-1][1]);
count++;
}
}
return count;
}
}
划分字母区间 LeetCode 763
题目链接:划分字母区间 LeetCode 763 - 中等
思路
无。
字符只出现在该区间,我们尽可能的分出更多的区间。
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> result = new LinkedList<>();
// 记录26个字母出现的最远的下标
int[] hash = new int[26];
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
hash[chars[i] - 'a'] = i;
}
// 记录划分片段的左右端点
int left = 0;
int right = 0;
for (int i = 0; i < chars.length; i++) {
right = Math.max(right, hash[chars[i] - 'a']);
if (right == i){
result.add(right - left + 1);
left = i + 1; // 下一次起始位置,左端点
}
}
return result;
}
}
总结
本题思路如图:
- 记录每个字母出现的最远位置
- 在该字母到出现最远位置中,有其他字母的最远位置超过了该字母,更新最远位置
- 到达最远位置就分为一段。
贪心贪的就是字母的最远位置,每次有更远的就更新,知道到达这最远的地方。
合并区间 LeetCode 56
题目链接:合并区间 LeetCode 56 - 中等
思路
和上面的题一样,但是多了一个合并的操作
class Solution {
public int[][] merge(int[][] intervals) {
// 先排序
Arrays.sort(intervals, (o1, o2) -> o1[0]-o2[0]);
List<int[]> list = new LinkedList<>();
// 记录有几个数加入了list
int count = 0;
// 先加入第一个数
list.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
int left = list.get(count)[0];
int right = list.get(count)[1];
if (right >= intervals[i][0]){
list.remove(count);
count--;
left = Math.min(left, intervals[i][0]);
right = Math.max(right, intervals[i][1]);
list.add(new int[]{left, right});
}else {
list.add(intervals[i]);
}
count++;
}
return list.toArray(new int[count][]);
}
}
优化:
- 题目已经是按左边界排好序了,所以根本不用判断left的大小,只用更新right为最大的值就行了。
- 不用另外定义一个count来记录list元素的位置,用list自带的getLast()与removeLast()
class Solution {
public int[][] merge(int[][] intervals) {
// 先排序
Arrays.sort(intervals, (o1, o2) -> o1[0]-o2[0]);
LinkedList<int[]> list = new LinkedList<>();
// 先加入第一个数
list.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
if (list.getLast()[1] >= intervals[i][0]){
int left = list.getLast()[0];
int right = Math.max(list.getLast()[1], intervals[i][1]);
// 由于要合并,需取出最后一个数组
list.removeLast();
list.add(new int[]{left, right});
}else {
list.add(intervals[i]);
}
}
return list.toArray(new int[list.size()][]);
}
}
总结
本题和前面几个题是一样的,都是有重叠区间类型的,我觉得主要分为几个不步骤:
-
先对区间进行
排序
,具体怎么排看要求类似:Array.sort(arr, (o1,o2) -> o1[0]-o2[0]
-
第一个数需不需要提前加入,需要就加入,不需要就直接循环区间,
循环的时候从i=1开始
。 -
每次判断完两个比较区间后,
更新左边界或者右边界
,随后进入下一判断的区间。