代码随想录 10.27 || 贪心 LeetCode 435.无重叠区间、763.划分字母区间、56.合并区间

435.无重叠区间

        给定一个区间的集合 intervals,其中 intervals[i] = [start, end]。返回 需要移除区间的最小数量,使剩余区间互不重叠

        看到本题的第一思路是排序,排序可以使重叠区域相邻,从而可以借助 for 循环遍历。问题来了,按照左区间排序,还是右区间排序?在本题中左右区间都可以,我选择左边界。排序过后,思路就和射气球问题类似,如果当前区间的左边界值小于上一个区间的右边界值,说明区这两个区间重叠,此时计数器自加。

class Solution {
private:
    static bool cmp(const vector &a, const vector &b) {
        return a[1] < b[1];
    }
public:
    int eraseOverlapIntervals(vector>& intervals) {
        if (intervals.size() == 1) return 0;
        sort(intervals.begin(), intervals.end());

        int result = 0;
        for (int i = 1; i < intervals.size(); i++) {
            if (intervals[i][0] >= intervals[i - 1][1]) {
                continue;
            } else {
                result++;
                intervals[i][1] = intervals[i - 1][1];
            }
        }
        return result;
    }
};

        注意,此时,还需要将当前区间的 右边界值 设置为 上一个右边界值 和 当前右边界值 中的最小值。

        为什么这么做,有的同学可能会问了,计数器自加,说明当前边界已经被删除了,为什么还要取两个边界中的最小值,而不是将当前边界的右边界值拷贝为上一个边界的右边界值?

        这里同学走入了一个误区,认为访问的是当前边界,所以删除的也是当前边界。其实不然,根据贪心算法的策略,全局问题为删除最少的区间,分解的局部子问题为删除重叠区间中,覆盖区间大的那个区间。因此需要取两个区间中的最小右边界值,将右边界值大的区间“删除”!!!

763.划分字母区间

        给你一个字符串 s。我们要把这个字符串分为尽可能多的片段,同一字母最多出现在一个片段中。注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s。返回一个表示每个字符串片段的长度的列表。

        全局问题,按照要求将给定字符串划分为尽可能多的片段;局部问题,保证每一个子串最短。如何确定子串的长度?题中要求,某一种字符只能出现在一个子串中,与重叠区间问题类似,我们可以通过求各个字符的区间长度,进而重叠字符串的的最短长度。

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

        vector result;
        int left = 0, 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;
    }
};

         在代码中,我们通过整数数组,存储字符串中每一种字符的最远位置,并随着遍历过程不断更新最原位置,一旦 i == right,说明当前子串内的所有字符都被包括了,此时返回子串的长度,并修正 left 的位置。

56.合并区间

        以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [start, end]。请你合并所有重叠的区间,并返回一个 不重叠的区间数组,该数组恰好覆盖输入中的所有区间。

        合并数组问题,类似射气球和无重叠无区间问题,删除数组中重叠的数组,废话不说,直接上代码。

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

public:
    vector> merge(vector> &intervals) {
        vector> result; result.clear();
        sort(intervals.begin(), intervals.end(), cmp);

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

        result.push_back(intervals[intervals.size() - 1]);
        return result;
    }
};

        前半部分和Carl哥的思路类似,根据数组的左边界值排序,聚集重叠区间。后半部逻辑稍有不同,在for中,我在if中判断当前区间和前一个区间是否重叠,如果不重叠则将前一个区间加入结果集中;如果重叠,则合并两个区间,扩容当前区间的范围。如此循环直至结束。此时,最后一个合并完毕的区间并没有加入结果集,因为我总是将前一个符合条件的区间加入结果集,因此在for后,将最后一个区间加入结果集。此时,最后一个区间也必定是不重叠区间。

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