Day 36 贪心算法 P5

435. 无重叠区间

代码随想录

1. 思路

这道题的思路和射箭非常像,因此可以直接利用射箭的结果。但其实复杂了,有更简单的方法。

(1)射箭——左排序

射箭这道题跳过的部分就是这道题需要计数的部分。但是射箭题right>=left需要跳过,本题只要right>left就可以计数。这是因为如果有这样两段,[1,2][2,3],射箭的时候,射到2就可以双穿,所以是>=,但是这道题中这两段交集为1并没有长度,因此不计数。说白了就是点和线的区别。

(2)射箭——逆转结果

可以直接将size()-射箭个数作为结果。但正如(1)中所说的,射箭的判断条件要从right<=left改成right

(3)右排序

因为重合片段个数最多为size,所以只要顺序后有两个不同线段有交集,就可以计数。所以,不用判断一共有多少个在一个重合中,可以直接遍历顺序右侧,看两两是否有交集。

之所以右排序,因为我们是从左到右遍历的,每跨越一个线段相当于抹除了左侧相交的线断,所以如果用左排序是不合理的。

class Solution {
public:
    static bool cmp(const vector& a, const vector& b) {
        return a[0] < b[0];
    }
    int eraseOverlapIntervals(vector>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
        int left = intervals[0][0];
        int right = intervals[0][1];
        int num = 0;
        for(int i=1; ileft){
                num++;
                right = min(right,intervals[i][1]);
            }else{
                right = intervals[i][1];
            }
        }
        return num;
    }
};

763. 划分字母区间

代码随想录

1. 思路

思路就是转化到上面那道题,先构建出顺序的区间,之后判断不重复的条件。条件就是当前面一个right<下面一个left。

但其实有更简单的方法。因为这个条件等价于,不同的线段属于不同堆。因此,可以记录字母最后一次出现的位置。然后从前往后遍历,如果遇到max==index,说明这属于一堆。相当于用位置对应位置进行记录。

class Solution {
public:
    vector partitionLabels(string S) {
        int hash[27] = {0}; // i为字符,hash[i]为字符出现的最后位置
        for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置
            hash[S[i] - 'a'] = i;
        }
        vector result;
        int left = 0;
        int right = 0;
        for (int i = 0; i < S.size(); i++) {
            right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
            if (i == right) {
                result.push_back(right - left + 1);
                left = i + 1;
            }
        }
        return result;
    }
};

56. 合并区间

代码随想录

1. 思路

我的思路是,记录重合区间的起始和重点位置,然后遍历,一个区域结束以后就保存结果

但是这样做有很多细节需要处理,比如最后一个区间如果是独立的,记录不上。比如只有两个区间,来不及更新。因此需要多写很多条件。

因此,有更简单的方法,就是在result里面不断更新,而不是更新后再存进去

在结果上更新在很多时候可以显著降低算法分类成本,但是需要算法是线性推进的。贪心算法本质上就是线性推进、全局最优的,因此可以这样做。比如之前震荡数列和气球在原数组上更新left或right,和这道题在result里更新。

class Solution {
public:
    vector> merge(vector>& intervals) {
        vector> result;
        if (intervals.size() == 0) return result; // 区间集合为空直接返回
        // 排序的参数使用了lambda表达式
        sort(intervals.begin(), intervals.end(), [](const vector& a, const vector& b){return a[0] < b[0];});

        // 第一个区间就可以放进结果集里,后面如果重叠,在result上直接合并
        result.push_back(intervals[0]); 

        for (int i = 1; i < intervals.size(); i++) {
            if (result.back()[1] >= intervals[i][0]) { // 发现重叠区间
                // 合并区间,只更新右边界就好,因为result.back()的左边界一定是最小值,因为我们按照左边界排序的
                result.back()[1] = max(result.back()[1], intervals[i][1]); 
            } else {
                result.push_back(intervals[i]); // 区间不重叠 
            }
        }
        return result;
    }
};

你可能感兴趣的:(贪心算法,算法)