算法刷题 Day 36 | ● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

今天的三道题目,都算是 重叠区间 问题,大家可以好好感受一下。 都属于那种看起来好复杂,但一看贪心解法,惊呼:这么巧妙!

还是属于那种,做过了也就会了,没做过就很难想出来。

不过大家把如下三题做了之后, 重叠区间 基本上差不多了

 435.无重叠区间

https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html

Tips:排序的写法很重要,根据什么东西来进行排序也很重要。

按照右边界排序,就要从左向右遍历,因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。

按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历。

如果按照左边界排序,还从左向右遍历的话,其实也可以,逻辑会有所不同。

我来按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了

此时问题就是要求非交叉区间的最大个数。

右边界排序之后,局部最优:优先选右边界小的区间,所以从左向右遍历,留给下一个区间的空间大一些,从而尽量避免交叉。全局最优:选取最多的非交叉区间。

局部最优推出全局最优,试试贪心!

这里记录非交叉区间的个数还是有技巧的,如图:

435.无重叠区间

区间,1,2,3,4,5,6都按照右边界排好序。

每次取非交叉区间的时候,都是可右边界最小的来做分割点(这样留给下一个区间的空间就越大),所以第一条分割线就是区间1结束的位置。

接下来就是找大于区间1结束位置的区间,是从区间4开始。那有同学问了为什么不从区间5开始?别忘了已经是按照右边界排序的了

区间4结束之后,再找到区间6,所以一共记录非交叉区间的个数是三个。

总共区间个数为6,减去非交叉区间的个数3。移除区间的最小数量就是3。

我的题解:

class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b){
        return a[1] < b[1];
    }

public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size() == 0) return 0;
        int count = 1;
        sort(intervals.begin(), intervals.end(), cmp);
        int rightEnd = intervals[0][1];
        for(int i = 1; i<intervals.size(); i++){
            if(intervals[i][0] >= rightEnd){
                count++;
                rightEnd = intervals[i][1];
            }
        }
        return intervals.size() - count;
    }
};

763.划分字母区间

https://programmercarl.com/0763.%E5%88%92%E5%88%86%E5%AD%97%E6%AF%8D%E5%8C%BA%E9%97%B4.html

Tips:

我的题解:

1. 最开始自己想的解法(英文是26个字母不是24个啊啊啊啊,就在这写错了没过)452.用最少数量的箭引爆气球 (opens new window)435.无重叠区间 (opens new window)相同的思路。

统计字符串中所有字符的起始和结束位置,记录这些区间(实际上也就是435.无重叠区间 (opens new window)题目里的输入),将区间按左边界从小到大排序,找到边界将区间划分成组,互不重叠。找到的边界就是答案。

class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b){
        return a[0] < b[0]; 
    }
public:
    vector<int> partitionLabels(string s) {
        vector<vector<int>> alphabetTemp(26);
        for(int i = 0; i<s.size(); i++){
            alphabetTemp[s[i] - 'a'].push_back(i);
        }

        vector<vector<int>> alphabet;

        for(int i = 0;i<26;i++){
            if(alphabetTemp[i].size() > 0){
                alphabet.push_back(alphabetTemp[i]);
            }
        }

        sort(alphabet.begin(),alphabet.end(),cmp);
        
        vector<int> result;
        int rightEnd = alphabet[0][alphabet[0].size()-1];
        int pre = -1;

        for(int i=1; i<alphabet.size();i++){
            if(alphabet[i][0] > rightEnd){
                result.push_back(rightEnd - pre);
                pre = rightEnd;
                rightEnd = alphabet[i][alphabet[i].size() - 1];
            }
            else{
                rightEnd = max(rightEnd,alphabet[i][alphabet[i].size() - 1]);
            }
        }
        result.push_back(rightEnd - pre);
        return result;
    }
};

2. 比较巧妙的解法,只关心每个字母最后一次出现的位置。

class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> endPos(28);
        for(int i = 0; i<s.size(); i++){
            endPos[s[i] - 'a'] = i;
        }

        int curEnd = 0;
        int lastEnd = -1;
        vector<int> result;

        for(int i = 0; i<s.size(); i++){
            curEnd = max(curEnd, endPos[s[i] - 'a']);
            if(curEnd == i){
                result.push_back(curEnd - lastEnd);
                lastEnd = curEnd;
            }
        }
        return result;
    }
};

56.合并区间

本题相对来说就比较难了。

https://programmercarl.com/0056.%E5%90%88%E5%B9%B6%E5%8C%BA%E9%97%B4.html

Tips:我咋觉得这题还挺简单的,先排个序,然后记录下左右端点,再合并一下就完事了

我的题解:

class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b){
        return a[0] < b[0];
    }    
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
        int begin = intervals[0][0];
        int end = intervals[0][1];
        vector<vector<int>> result;
        for(int i = 1; i < intervals.size(); i++){
            if(intervals[i][0] > end){
                vector<int> temp;
                temp.push_back(begin);
                temp.push_back(end);
                result.push_back(temp);
                begin = intervals[i][0];
                end = intervals[i][1];
            }
            else{
                end = max(intervals[i][1], end);
            }
        }
        vector<int> temp;
        temp.push_back(begin);
        temp.push_back(end);
        result.push_back(temp);
        return result;
    }
};
posted @ 2023-02-10 21:56  GavinGYM  阅读(42)  评论(0)    收藏  举报