一、LeetCode435. 无重叠区间
题目链接:435. 无重叠区间
题目描述:
给定一个区间的集合 intervals
,其中 intervals[i] = [starti, endi]
。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ] 输出: 2 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: intervals = [ [1,2], [2,3] ] 输出: 0 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
提示:
1 <= intervals.length <= 105
intervals[i].length == 2
-5 * 104 <= starti < endi <= 5 * 104
算法分析:
按照区间的有边界大小进行排序。
然后判断上一个区间的右边界是否在当前区间内部。如果是,说明两区间重叠了,删掉该区间,再继续判断下一个;如果没有在区间内部说明没有重叠,两个区间都是合理的,判断下一个区间。
代码如下:
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals, (s1, s2) -> s1[1] - s2[1]);//对区间数组按照区间右边界的位置大小进行排序
int lastEnd = intervals[0][1];//上一个合理区间的有边界位置
int count = 0;
for(int i = 1; i < intervals.length; i++) {
//如果当前区间的左边界小于上一个合理区间的右边界(即上一个合理区间与该区间就发生重叠)移除区间,否则两区间不发生重叠,当前区间合理,将当前区间的右边界赋给lastEnd,继续去判断下一个区间
if(lastEnd > intervals[i][0]) count++;//
else lastEnd = intervals[i][1];
}
return count;
}
}
二、LeetCode763. 划分字母区间
题目链接:763. 划分字母区间
题目描述:
给你一个字符串 s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s
。
返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:s = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释: 划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = "eccbbbbdec" 输出:[10]
提示:
1 <= s.length <= 500
s
仅由小写英文字母组成
算法分析:
标记每个字符在字符串中出现的末尾位置。
然后遍历整个字符串,更新每个字符所辐射的最大范围。
如果当前下标就等于之前字符辐射的最大范围的话,那么当前的位置就是一个字符串的分割点。
代码如下:
class Solution {
public List<Integer> partitionLabels(String s) {
int[] arr = new int[26];//用来记录每个字符最后一次出现的位置
for(int i = 0; i < s.length(); i++)
arr[s.charAt(i) - 'a'] = i;
List<Integer>list = new ArrayList<>();
int left = 0;//字符串片段的起始位置
int right = 0;//字符串片段结束位置
for(int i = 0; i < s.length(); i++) {//每次更新每个字符串片段所能辐射的最远位置。
right = Math.max(arr[s.charAt(i) - 'a'], right);
if(i == right) {//如果字符串片段结束的位置刚好是当前的位置,将这段字符串长度放到数组当中,然后寻找下一段字符串的长度
list.add(right - left + 1);
left = i + 1;
}
}
return list;
}
}
三、LeetCode56. 合并区间
题目链接:56. 合并区间
题目描述:
以数组 intervals
表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi]
。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 输出:[[1,6],[8,10],[15,18]] 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]] 输出:[[1,5]] 解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
提示:
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
算法分析:
这道题需要先对每个区间按照左区间的大小排序,然后再来进行合并重复区间。
这里有三种情况分别需要进行处理:
一是如果上一个区间的末尾在当前区间的右边说明当前区间是完全包含于上一个区间的,此时无需任何操作;
二是如果上一个区间的末尾是在当前区间内部,那么就需要对两个区间进行合并,新的区间左区间是上一个区间的左区间,右区间就变成当前区间的右区间了。
三是如果上一区间的右区间在当前区间的左边,那么两区间就没有重叠部分,将上一个区间放到结果集里,然后在寻找下一个无重叠区间。
注意这里说的“上一个区间”是一个不断变动的区间,用来确定每一个合并之后不在于其他区间重叠的区间。
代码如下:
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (s1, s2) -> s1[0] - s2[0]);//按照左区间的从小到大进行排序
int start = intervals[0][0];//合并后的左区间
int end = intervals[0][1];//合并后的右区间
LinkedList<int[]>res = new LinkedList<>();//放不重叠的区间数组
for(int i = 1; i < intervals.length; i++) {
if(end > intervals[i][1]) continue;//如果上一个右区间的大于当前区间的右区间,说明当前区间完全包含于上一个区间。
else if(end >= intervals[i][0] && end <= intervals[i][1]) {//如果上一个区间的右区间在当前区间内部,说明两个区间有重叠,进行合并,合并后的左区间不变,右区间扩大到当前区间的右区间大小
end = intervals[i][1];
}
else {//如果上一个区间的右区间小于当前区间的左区间,说明两个区间没有交集,不需要合并,得到一个没有重叠的区间,将其放入结果集,在更新下一个区间的左右区间
int[] a = new int[2];
a[0] = start;
a[1] = end;
res.add(a);
start = intervals[i][0];
end = intervals[i][1];
}
}
//将最后一个区间放入结果集当中
int[] a = new int[2];
a[0] = start;
a[1] = end;
res.add(a);
return res.toArray(new int[res.size()][2]);//将结果集转化为数组返回
}
}
总结
一三题的解法类似,比较容易做出来,第二题的话,利用每个字符的辐射范围来确定字符串片段的长度这个方法不容易想出来。