代码随想录算法训练营第三十六天| LeetCode 435. 无重叠区间、763.划分字母区间、56. 合并区间

一、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),排序需要的空间开销

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值