● 435. 无重叠区间
解法1:
本题其实和452.用最少数量的箭引爆气球 (opens new window)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
class Solution {
public:
static bool cmd(vector<int>& v1,vector<int>& v2){
return v1[0]<v2[0];//左边界排序
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),cmd);
int result = 1;
for(int i = 1;i<intervals.size();i++){
if(intervals[i][0] >= intervals[i-1][1]){
result++;
}else{
intervals[i][1] = min(intervals[i][1],intervals[i-1][1]);
}
}
return intervals.size() - result;
}
};
解法二:
按左边界排序,然后记录重叠数量
class Solution {
public:
static bool cmd(vector<int>& v1,vector<int>& v2){
return v1[0]<v2[0];//左边界排序
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),cmd);
int count = 0;//记录重叠区间
for(int i = 1;i<intervals.size();i++){
if(intervals[i][0]<intervals[i-1][1]){//重叠了
count++;
intervals[i][1] = min(intervals[i][1],intervals[i-1][1]);
}
}
return count;
}
};
● 763.划分字母区间
一想到分割字符串就想到了回溯,但本题其实不用回溯去暴力搜索。
题目要求同一字母最多出现在一个片段中,那么如何把同一个字母的都圈在同一个区间里呢?
如果没有接触过这种题目的话,还挺有难度的。
在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:
- 统计每一个字符最后出现的位置
- 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
如图:
明白原理之后,代码并不复杂,
class Solution {
public:
vector<int> partitionLabels(string s) {
int hash[27] = {0};// i为字符,hash[i]为字符出现的最后位置
for(int i = 0;i<s.size();i++){// 统计每一个字符最后出现的位置
hash[s[i]-'a'] = i;
}
int left = 0,right = 0;
vector<int> result;
for(int i = 0;i<s.size();i++){
right = max(right,hash[s[i]-'a']);// 找到字符出现的最远边界
if(i == right){
result.push_back(right - left + 1);
left = right + 1;
}
}
return result;
}
};
● 56. 合并区间
本题的本质其实还是判断重叠区间问题。
大家如果认真做题的话,话发现和我们刚刚讲过的452. 用最少数量的箭引爆气球 (opens new window)和 435. 无重叠区间 (opens new window)都是一个套路。
这几道题都是判断区间重叠,区别就是判断区间重叠后的逻辑,本题是判断区间重贴后要进行区间合并。
所以一样的套路,先排序,让所有的相邻区间尽可能的重叠在一起,按左边界,或者右边界排序都可以,处理逻辑稍有不同。
按照左边界从小到大排序之后,如果 intervals[i][0] <= intervals[i - 1][1]
即intervals[i]的左边界 <= intervals[i - 1]的右边界,则一定有重叠。(本题相邻区间也算重贴,所以是<=)
这么说有点抽象,看图:(注意图中区间都是按照左边界排序之后了)
知道如何判断重复之后,剩下的就是合并了,如何去模拟合并区间呢?
其实就是用合并区间后左边界和右边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把原区间加入到result数组。
class Solution {
public:
static bool cmd(const vector<int>& v1,const vector<int>& v2){
return v1[0]<v2[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),cmd);
vector<vector<int>> result;
result.push_back(intervals[0]);
for(int i = 1;i<intervals.size();i++){
if(intervals[i][0]<=result.back()[1]){//区间重叠了需要合并
result.back()[1] = max(intervals[i][1],result.back()[1]);
}else{//区间没有重叠,直接插入
result.push_back(intervals[i]);
}
}
return result;
}
};