【Day36】代码随想录之贪心_435. 无重叠区间 _763.划分字母区间 _56. 合并区间

文章目录

      • 435. 无重叠区间
      • 763.划分字母区间
      • 56. 合并区间

435. 无重叠区间

参考文档:代码随想录

题目:

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

  • 示例 1:
    输入: [ [1,2], [2,3], [3,4], [1,3] ]
    输出: 1
    解释: 移除 [1,3] 后,剩下的区间没有重叠。

分析:
左区间排序出现的问题:
忽略了vector按照左边界排序,结果左边是从小到大,不会重叠的,遇到重叠处,两个的左边界与前面都不重叠,最后留下哪一个,取决于哪一个的右边界更小,与后面重叠的概率就越小。

代码:
左区间从小到大排序,得出需要去掉的总数sum

class Solution {
    static bool cmp(const vector<int>& a, const vector<int>& b){
        if(a[0] != b[0]) return a[0] < b[0];
        return a[1] < b[1];
    }
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        //重叠:上一个的右区间 大于 下一个的左区间
        //不用删除,只需要记录和跳过
        //如何选择区间,左边界相同的情况下,保留右边界更小的
        sort(intervals.begin(), intervals.end(), cmp);//从小到大
        // 按照左边界排序,左边一定是有序的
        int sum = 0;
        int pre = intervals[0][1];//保存上个右边界值
        for (int i = 1; i < intervals.size(); i++){
            if (pre > intervals[i][0]) {//有重叠,看需要保存哪一个
                sum++;
                pre = min(pre, intervals[i][1]);
            }
            else{//无重叠
                pre = intervals[i][1];
            }
        }
        return sum;
    }
};

右边界排序:为什么排序后第一个一定是符合的区间。

class Solution {
    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;
        sort(intervals.begin(), intervals.end(), cmp);
        int sum = 1;
        int pre = intervals[0][1];
        //为什么第一个一定是可以的呢,按照右边界排序,最小的右边界最小,影响的最小
        for (int i = 1; i < intervals.size(); i++){
            if(pre <= intervals[i][0]){ 
                sum++;
                pre = intervals[i][1];
            }
        }
        return (intervals.size() - sum);
    }
};

763.划分字母区间

参考文档:代码随想录

题目:

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

  • 示例:
    输入:S = “ababcbacadefegdehijhklij”
    输出:[9,7,8] 解释: 划分结果为 “ababcbaca”, “defegde”, “hijhklij”。 每个字母最多出现在一个片段中。 像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。
  • 提示:
    S的长度在[1, 500]之间。
    S只包含小写字母 ‘a’ 到 ‘z’ 。

分析:
直接想法是统计每个位置的字母最后出现的位置,在这个区间内如果有更大的位置,字符串的长度就要往后增加,终止条件是到达这个区间内字母最后出现位置的最大值。
难点1是想清楚终止条件,难点2是统计字母最后出现的位置,难点3是确定该区间字母出现的最大位置。

  1. 终止条件是到达这个区间内字母最后出现位置的最大值。即统计的出现的最后位置 == 该字母所处的位置。
  2. 由于字母a-z,每个字母出现的位置就是该字母的数组下标。重复查找的更新操作,有限区间,所以用哈希搜索。int num[27]; num[s[i] - ‘a’] = i。
  3. 有1可知结束位置是 num[s[i] - ‘a’] == i; 在i位置之前出现的字母如果最后出现位置大于i需要更新i。if(num[s[i] - ‘a’] > max) max = num[s[i] - ‘a’]; 在max 等于 i处找到上一个字符串的截取位置。

代码:

class Solution {
public:
    vector<int> partitionLabels(string s) {
        //统计每个字母最后出现的位置,查找需要快,unordered_map,由于26个字母有序,用数组
        int num[27] = {0};
        vector<int> res;
        for(int i = 0; i < s.size(); i++){
            int table = s[i] - 'a';
            num[table] = i;
        }
        //在这个区间内最远的位置(字母最后出现的位置)
        int max = 0;//区间的最大值
        int part = 0;//分割的片段个数
        for(int i = 0; i < s.size(); i++){
            part++;
            if(num[s[i] - 'a'] > max) max = num[s[i] - 'a'];
            if(max == i) {
                res.push_back(part);
                part = 0;
            }
        }
        return res;
    }
};

56. 合并区间

参考文档:代码随想录

题目:

给出一个区间的集合,请合并所有重叠的区间。

  • 示例 1:
    输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
    输出: [[1,6],[8,10],[15,18]]
    解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

分析:
找到有重叠的区间,更新左右边界。
定一动一:先按照左边界从小到大排列,使得左边界的大小递增固定。在更新右边界的时候,如果遇到 上一个的右边界 大于 下一个的左边界,说明有重叠,需要更新右边界。否则,保存上一个的并集,再找下一个区间。

代码:

class Solution {
    static bool cmp(const vector<int>& a, const vector<int>& b){
        if(a[0] != b[0]) return a[0] < b[0];
        return a[1] < b[1];
    }
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        //找到重叠的区间,当不重叠的时候合并之前的区间
        vector<vector<int>> res;
        if(intervals.size() == 0) return res;

        sort(intervals.begin(), intervals.end(), cmp);
        
        int pre = intervals[0][0], cur = intervals[0][1];
        for(int i = 1; i < intervals.size(); i++){
            if(cur >= intervals[i][0]){
                cur = max(cur, intervals[i][1]);
            }
            else{
                res.push_back({pre, cur});
                pre = intervals[i][0];
                cur = intervals[i][1];
            }
        }
        res.push_back({pre, cur});
        return res;
    }
};

你可能感兴趣的:(算法,数据结构,leetcode,力扣,C++)