435. 无重叠区间
思路:
1. 按左边界排序,从左到右遍历;用count记录需要移除的区间;
2. 若当前遍历区间的左边界小于前一个区间的右边界时,count++,更新当前区间右边界为作比较的这两个区间右边界的最小值;
3. 遍历结束,返回count;
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
int count = 0;
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]<intervals[i-1][1]){
count++;
intervals[i][1]=Math.min(intervals[i-1][1],intervals[i][1]);
}
}
return count;
}
}
思路2:
1. 按右边界从小到大排序,从左到右遍历;局部最优:遍历,留给下一个区间的空间大一些,从而尽量避免交叉。全局最优:选取最多的非交叉区间。
2. 用edge表示分割线(右边界),初始化为Integer.MIN_VALUE。若当前遍历区间左边界<分割线,说明有交集,count++;若当前遍历区间左边界>=分割线,说明无交集,更新分割线为当前遍历区间的右边界;
3. 遍历结束,返回count。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals,(a,b)->Integer.compare(a[1],b[1]));
int count = 0;
int edge = Integer.MIN_VALUE;
for(int i=0;i<intervals.length;i++){
if(edge<=intervals[i][0]){//无交集
edge=intervals[i][1];
}else{//有交集(不更新edge)
count++;
}
}
return count;
}
}
763.划分字母区间
思路:
1. 统计每个字母最后出现的位置;
2. 从头遍历字符,并更新字符的最远出现位置下标;若最远出现位置下标与当前下标相等,则找到了分割点。
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> res = new LinkedList<>();
int[] edge = new int[26];
char[] chars = s.toCharArray();
for(int i=0;i<chars.length;i++){
edge[chars[i]-'a']=i;
}
int idx = 0;
int last = -1;
for(int i=0;i<chars.length;i++){
idx=Math.max(idx,edge[chars[i]-'a']);
if(i==idx){
res.add(i-last);
last=i;
}
}
return res;
}
}
思路2:无重叠区间
1. 统计字符串中所有字符的起始和结束位置,记录为区间;
2. 按照左边界从小到大排序,找到边界将区间划分互不重叠的组;
3. 返回边界合集;
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> res = new ArrayList<>();
int[][] partitions = findPartitions(s);
Arrays.sort(partitions,(o1,o2)->Integer.compare(o1[0],o2[0]));
int right = partitions[0][1];//记录最大右边界
int left = 0;
for(int i=0;i<partitions.length;i++){
if(partitions[i][0]>right){//分割
res.add(right-left+1);
left = partitions[i][0];
}
right = Math.max(right,partitions[i][1]);
}
res.add(right-left+1);
return res;
}
public int[][] findPartitions(String s){
//记录各个字母左右边界
int[][] hash = new int[26][2];
for(int i=0;i<s.length();i++){
char c = s.charAt(i);
if(hash[c-'a'][0]==0) hash[c-'a'][0]=i;//左边界
hash[c-'a'][1]=i;//右边界
hash[s.charAt(0)-'a'][0] = 0;//第一个元素左边界
}
//去除字符串中未出现的字母所占用区间
List<Integer> temp = new ArrayList<>();
List<List<Integer>> h = new LinkedList<>();
for(int i=0;i<26;i++){
temp.clear();
temp.add(hash[i][0]);
temp.add(hash[i][1]);
h.add(new ArrayList<>(temp));
}
int[][] res = new int[h.size()][2];
for(int i=0;i<h.size();i++){
List<Integer> list = h.get(i);
res[i][0] = list.get(0);
res[i][1] = list.get(1);
}
return res;
}
}
56. 合并区间
思路:
1. 按照左边界从小到大排序;
2. 局部最优:每次合并都取最大的右边界,这样就可以合并更多的区间;整体最优:合并所有重叠的区间;
3. 遍历区间;若当前所遍历的区间的左边界小于等于前一个区间的右边界,合并为一个新区间,加入result数组;若没有合并,则将原数组加入result数组。
class Solution {
public int[][] merge(int[][] intervals) {
LinkedList<int[]> res = new LinkedList<>();
Arrays.sort(intervals,(o1,o2)->Integer.compare(o1[0],o2[0]));
res.add(intervals[0]);
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]<=res.getLast()[1]){
int start = res.getLast()[0];
int end = Math.max(intervals[i][1],res.getLast()[1]);
res.removeLast();
res.add(new int[]{start,end});
}else{
res.add(intervals[i]);
}
}
return res.toArray(new int[res.size()][]);
}
}