一、LeetCode 435. 无重叠区间
题目链接/文章讲解/视频讲解:https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html
状态:已解决
1.思路
本题的局部最优是尽量移除与某个区间重叠的其他区间,全局最优是移除的区间数最少,局部最优可以推出全局最优,因此可以贪心。如图
这种情况当然就只移除第二个区间,这样就刚好使得剩下的区间不重叠。
这种情况无论如何都至少要移除两个区间。因此对于k个重叠区间,最少都需要移除(k-1)个区间,才能使得剩余区间并不重叠。
因此,此题和452题思路都一致,就是寻找重叠区间,只是这题重叠区间要保留一份,而452题是重叠区间用一只箭射掉,二者统计得到的数量是一致的。故求未重叠的区间的数量的代码是一模一样的,本题再用所有区间的数量-未重叠区间的数量,就能得到要移除的重叠区间的数量。
2.代码实现
class Solution {
public:
static bool cmp(const vector<int>& v1, const vector<int>& v2){
//第一元素相等时,再比较第二元素
if (v1[0] == v2[0])
return v1[1] < v2[1];
return v1[0] < v2[0];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
int num = 1;
sort(intervals.begin(),intervals.end(),cmp);
for(int i=1;i<intervals.size();i++){
if(intervals[i][0]>=intervals[i-1][1]){
num++;
}else{
intervals[i][1] = min(intervals[i][1],intervals[i-1][1]);
}
}
return intervals.size()-num;//相当于452题多出了一步求移除区间的数量,二题是对称的
}
};
二、 763.划分字母区间
题目链接/文章讲解/视频讲解:https://programmercarl.com/0763.%E5%88%92%E5%88%86%E5%AD%97%E6%AF%8D%E5%8C%BA%E9%97%B4.html
状态:已解决
1.思路
一般说到分割字符串问题,我们下意识就想到回溯算法,但回溯算法效率较低,一般还是优先采用效率较高的算法。此题就可以不用回溯。
此题要求同一字母最多出现在同一个片段中,那么如何保证某个字母只出现在同一个片段中呢?很简单,我们在划分片段的时候,就选取这个字母第一次出现的位置和它最远出现的位置,这样划分出来的其他片段就不会存在该字母了。如果在这个划分区间内,有其他字母出现,那么这个片段的右边界就不一定取这个字母的最远位置了,而是取这个字母开始位置到这个字母的最远位置之间,所有字母的最远位置的最大值。如图:
此时取得是a的起始位置和c的最远位置。
想明白这个点,接下来就很好处理了,可以分为如下两步:
- 统计每一个字符最后出现的位置
- 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
2.代码实现
class Solution {
public:
vector<int> partitionLabels(string s) {
int hash[27]={0};
vector<int> result;
for(int i=0;i<s.size();i++)
{
hash[s[i]-'a'] = i;
}
int left=0,right=0;
for(int i=0;i<s.size();i++){
right = max(hash[s[i]-'a'],right);
if(i == right){
result.push_back(right-left+1);
left = i + 1;
}
}
return result;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
三、56. 合并区间
题目链接/文章讲解/视频讲解:https://programmercarl.com/0056.%E5%90%88%E5%B9%B6%E5%8C%BA%E9%97%B4.html
状态:已解决
1.思路
我想的比较直接,这道题很明显还是一个找重叠区间的题,只不过前面的题都是计算去掉重叠区间后未重叠区间的数量,或者是,数轴上分开的区间的数量;而此题是要求你找到重叠区间后做合并操作。怎么合并?我的想法是延申上题的做法,用一个变量来记录每次重叠的左边界,然后找到重叠的区间(intervals[i][0] <= intervals[i-1][1]),就合并区间(具体操作是取intervals[i][1] 和intervals[i-1][1]中的最大值,即更新区间右边界)。
2.代码实现
class Solution {
public:
static bool cmp(const vector<int>& v1, const vector<int>& v2){
//第一元素相等时,再比较第二元素
if (v1[0] == v2[0])
return v1[1] < v2[1];
return v1[0] <= v2[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.size()==1) return intervals;
sort(intervals.begin(),intervals.end(),cmp);
int left = intervals[0][0];
vector<vector<int>> result;
for(int i=1;i<intervals.size();i++){
if(intervals[i][0]>intervals[i-1][1]){//与前面区间不重叠
result.push_back({left,intervals[i-1][1]});//将前面的重叠区间存入结果数组
left = intervals[i][0];//更新新的重叠区间的左边界
}
else{
intervals[i][1] = max(intervals[i][1],intervals[i-1][1]);//合并重叠区间,也就是更新右边界
}
}
result.push_back({left,max(intervals[intervals.size()-1][1],intervals[intervals.size()-2][1])});//最后一个区间是根据右边界是否在前面重叠区间中来考虑的。
return result;
}
};
时间复杂度: O(nlogn)
空间复杂度: O(logn),排序需要的空间开销