代码随想录算法训练营第三十六天|435. 无重叠区间、763.划分字母区间、56. 合并区间

LeetCode 435. 无重叠区间

链接:435. 无重叠区间

思路:

这道题的思路可以说是和452. 用最少数量的箭引爆气球一模一样,都是求重复区间的数量,移除区间的最小数量就等于所有区间的数量减去不重复区间的数量,减去所有重叠区间后剩余的则都是不重叠的区间。

这里采用右区间排序的办法。在左区间排序中,我们需要不断地更新rightmost,因为每次把新区间“纳入“当前区间范围内时,都需要更新rightmost到当前范围内包含的所有区间的右区间的最小值,但是在右区间排序就不用了,因为在右区间排序下,第一个位置正好就是最小的,所以只需要考虑下一个区间不在当前范围内的情况就行了。当下一个区间不在当前范围内时,更新rightmost区间并把不重复区间数量加一即可,和左区间排序的操作是一样的。最后再把所以区间数量减去不重复区间数量就行了。

代码:

class Solution {
public:
    static bool cmp(const vector& lhs, const vector& rhs)
    {
        return lhs[1] < rhs[1];
    }
    int eraseOverlapIntervals(vector>& points) {
        sort(points.begin(), points.end(), cmp);
        int count = 1;
        // 初始箭位置
        int rightmost = points[0][1];
        for (int i = 1; i < points.size(); i++)
        {
            // 如果不在当前范围内,更新当前的范围并增加区间的数量
            if (rightmost <= points[i][0])
            {
                rightmost = points[i][1];
                count++;
            }
        }
        return points.size() - count;
    }
};

LeetCode 763.划分字母区间

链接:763.划分字母区间

思路:

又是一道区间类问题,但是不能按照之前的方法做了,因为这里要处理的并不是重叠区间。一个字母只能出现在一个区间内,也就是说只要记录下每个字母最后出现的位置,那么在此之后的位置对于该字母来说都是可分割的,换句话说,在这个位置之前是一定不能分割的。所以我们需要一个哈希表来记录26个字母出现的最后位置。可以通过遍历字符串的方法更新每个字母的最后位置。

然后再次遍历字符串,用一个变量rightmost记录下当前的分割点。判断当前是否可以分割的标准为:当所有经过的字母最后出现的位置都小于等于当前下标i,则说明所有字母最后都出现在i以内,所以用rightmost表示当前经过的字母的最远下标,每次遍历都更新rightmost,取最大值,当i等于rightmost说明走到了分割点。同时我们还需要一个变量last用于储存每个区间的头的位置,因为答案要求的是每个区间的长度,所以还需要用当前下标i+1后减去头位置才是当前区间的长度。

代码:

class Solution {
public:
    vector partitionLabels(string s) {
        vector hashmap(26);
        for (int i = 0; i < s.size(); i++)
            hashmap[s[i] - 'a'] = i;
        int rightmost = 0;
        int last = 0;
        vector ans;
        for (int i = 0; i < s.size(); i++)
        {
            // 更新最远下标
            rightmost = max(hashmap[s[i] - 'a'], rightmost);
            // 如果当前走到了最远下标
            if (i == rightmost)
            {
                ans.push_back(i + 1 - last);
                last = i + 1;
            }
        }
        return ans;
    }
};

LeetCode 56. 合并区间

链接:56. 合并区间

思路:

又是一道重叠区间问题,按照之前重叠区间问题的做法,显然是需要排序的。同样,按照左区间排序或者右区间排序都是可行的,区别只是处理的逻辑稍微有些不一样。这里以左区间排序为例,按从小到大排列,然后用一个变量rightmost记录下当前区间的右区间的最大值,从前往后遍历寻找重复区间,区别是这里我们要使合并的区间最大,所以要取右区间最大值,而不是向之前箭射气球那道题一样取最小值。

当发现下一个区间不在当前区间范围内了,说明需要一个新的区间,更新rightmost的值为新的区间的最大右区间,同时还需要把旧的区间的最大右区间也一起更新了。我们用cur表示旧区间的下标,初始时为0,当发现有新的不重叠的区间,就更新cur为新的不重叠区间的下标。

最后再次遍历数组,因为这时候我们已经更新了最大区间,把重叠的区间全部都合并了,所以只需要把更新过的区间push进最终结果里,其他的区间直接continue就可以了。

代码:

class Solution {
public:
    static bool cmp(const vector& lhs, const vector& rhs)
    {
        return lhs[0] < rhs[0];
    }
    vector> merge(vector>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
        int rightmost = intervals[0][1];
        int cur = 0;
        for (int i = 1; i < intervals.size(); i++)
        {
            if (rightmost >= intervals[i][0])
                // 左排序取右最大值
                rightmost =  max(rightmost,intervals[i][1]);
            else
            {
                intervals[cur][1] = rightmost;
                cur = i;
                rightmost = intervals[i][1];
            }
        }
        intervals[cur][1] = rightmost;
        vector> ans;
        ans.push_back(intervals[0]);
        for (int i = 1; i < intervals.size(); i++)
        {
            if (intervals[i][1] <= ans[ans.size() - 1][1])
                continue;
            ans.push_back(intervals[i]);
        }
        return ans;
    }
};

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,数据结构)