力扣● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间 ●738. 单调递增的数字

文章讨论了解决区间相关问题的方法,如计算无重叠区间的数量、划分字母区间和保持单调递增的数字。主要涉及排序、比较边界、使用哈希映射优化和贪心策略的应用。
摘要由CSDN通过智能技术生成

● 435. 无重叠区间

跟弓箭题一样的原理:先集体对左边界排序,然后从第1个区间开始,当下一个区间的左边界比该区间的右边界要小的时候,就得去掉这个区间(count++),然后应该①直接更新该区间的右边界为和他重叠的所有区间的最小右边界(取这个和上一个的较小值,会使得更新的右边界越来越小,直到最小)。或者②维护一个right代表这个最小右边界,每一次统计之后更新这个right。

直接改:

class Solution {
public:
    static bool cmp(vector<int> a,vector<int> b){
        return a[0]<b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.empty())return 0;
        sort(intervals.begin(),intervals.end(),cmp);    //左边界从小到大排
        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][1],intervals[i][1]);
            }
        }
        return count;
    }
};

使用变量right(注意下一个区间和这个不重叠的话要更新一下right为现在区间的右边界):

class Solution {
public:
    static bool cmp(vector<int> a,vector<int> b){
        return a[0]<b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.empty())return 0;
        sort(intervals.begin(),intervals.end(),cmp);    //左边界从小到大排
        int count=0,right=intervals[0][1];
        for(int i=1; i<intervals.size(); ++i){
            if(intervals[i][0]<right){
                count++;
                right=min(right,intervals[i][1]);
            }
            else{
                right=intervals[i][1];
            }
        }
        return count;
    }
};

● 763.划分字母区间

最主要的:每个节点的最远出现位置都要先求出来,挨个统计。

在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。

可以分为如下两步:

  • 统计每一个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点

方案一:不用哈希映射(时间开销大)

class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> result;
        int right=INT_MIN,left=-1;//left是上一次的right,最远距离
        for(int i=0; i<s.size(); ++i){
            int j=s.size()-1;
            for(; j>=0; --j){
                if(s[j]==s[i]){
                    break;          //j是s[i]最远的出现距离
                }
            }
            right=max(right,j);     //更新
            if(i==right){           //找到之前字符最大出现位置和下标相等
                result.push_back(right-left);
                left=right;
            }
        }
        return result;
        
    }
};

用哈希映射:

class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> result;
        map<char,int> hash;
        for(int i=0; i<s.size(); ++i){
            hash[s[i]]=i;
        }
        int right=hash[s[0]],left=-1;
        for(int i=0;i<s.size();++i){
            right=max(hash[s[i]],right);
            if(right==i){
                result.push_back(right-left);
                left=right;
            }
        }
        return result;
        
    }
};

● 56. 合并区间

同样的,先对区间的左边界排序。这个题需要设置两个变量来保存重叠区间的最左边界(left)和最右边界(right),left和right分别初始化为第一个区间的左右边界,之后每遇到一个区间,如果这个区间左边界在right左边,意味着遇到了跟[left,right]重叠的区间,所以要更新right为最右边界,所以取max。否则的话跟之前的区间不重叠,那么可以把之前的[left,right]先保存到result,然后再更新left和right,意味着重新统计下一个重叠区间。

最后的[left,right]需要在循环外面保存。

class Solution {
public:
    static bool cmp(const vector<int> & a,const vector<int> & b){
        return a[0]<b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {        
        vector<vector<int>> result;
        sort(intervals.begin(),intervals.end(),cmp);
        int left=intervals[0][0],right=intervals[0][1];
        for(int i=0; i<intervals.size(); ++i){
            if(intervals[i][0]<=right){     //重叠
                right=max(intervals[i][1],right);
            }
            else{               //不重叠
                result.push_back(vector<int>{left,right});
                left=intervals[i][0];
                right=intervals[i][1];
            }
        }
        result.push_back(vector<int>{left,right});
        return result;
    }
};

在代码随想录中,不维护两个自定义变量,而是维护result的最后一个区间,如果Intervals下一个区间跟result最后这个区间有重叠,我们应该更新result的最后这个区间(因为左边界排过序了所以只更新最右边界result.back()[1])。否则作为一个新的统计重叠的区间加入到result。

class Solution {
public:
    static bool cmp(const vector<int> & a,const vector<int> & b){
        return a[0]<b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {        
        vector<vector<int>> result;
        sort(intervals.begin(),intervals.end(),cmp);
        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;
    }
};

●738. 单调递增的数字

一对一对地改,比如32,先3减1,然后2变成9,所以结果是29。这是两位数的情况,那么两位数以上的呢,从左往右还是从右往左,例子:332,从左往右的话结果是329,错误,第一对33是符合的,处理第二对32的时候修改了之前符合条件的3,所以只能从右往左一对一对地改。遇到一对不符合非递减条件,就把前一个元素减减,然后当前元素及其之后的全部改成9(如果只把当前元素改9,不能保证非递减,结果也不一定是最大)。所以用flag记录要改成9的起点,初始化为size(),flag没有修改的话之后就没有元素会被修改成9。

这题细节方面:从变量flag到结尾全部设置为9;flag的初始值。

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        //后往前,一对一对地改
        string str=to_string(n);
        int flag=str.size();
        for(int i=str.size()-1; i>0; --i){
            if(str[i]<str[i-1]){
                str[i-1]--;//前一位减1
                flag=i;     //后一位以后的都变成9
            }
        }
        for(int i=flag;i<str.size();++i){
            str[i]='9';//flag以及以后的全部赋值为9,flag没有改动不会改动str
        }
        return stoi(str);
    }
};

贪心一刷到此结束,根据代码随想录来总结一下:

局部最优推全局最优,for循环体里面处理局部的。严格证明不容易,想不到反例就是对的。对于贪心,我们不用一定要像数学证明题一样证明出来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值