Leetcode一类区间问题总结

 

目录

56. 合并区间

57. 插入区间

352. 将数据流变为多个不相交区间

435. 无重叠区间

436. 寻找右区间

452. 用最少数量的箭引爆气球

646. 最长数对链

715. Range 模块

729. 我的日程安排表 I

731. 我的日程安排表 II

732. 我的日程安排表 III

757. 设置交集大小至少为2


56. 合并区间

先按照左端点排序,左端点相同按照右端点排序,依次合并区间即可。

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        typedef pair<int, int> PII;
        int n = intervals.size();
        vector<PII> v;
        for (auto& x: intervals) v.push_back({x[0], x[1]});
        sort(v.begin(), v.end());
        int s = v[0].first, e = v[0].second;
        vector<vector<int>> res;
        for (int i = 1; i < n; i++) {
            if (v[i].first > e) {
                //开启一个新区间
                res.push_back({s, e});
                s = v[i].first;
                e = v[i].second;
            } else {
                e = max(e, v[i].second);
            }
        }
        //记得加入最后一个区间
        res.push_back({s, e});
        return res;
    }
};

 

57. 插入区间

给了一组排序好的区间,需要加入一个新区间,并且保持有序。

class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& a, vector<int>& b) {
        vector<vector<int>> res;
        int k = 0, n = a.size();
        //先把b区间左边和b没有交集的加入到答案里
        while (k < n && a[k][1] < b[0]) res.push_back(a[k++]);
        //合并有交集的区间,并加入到答案里
        if (k < n) {
            b[0] = min(b[0], a[k][0]);
            while (k < n && a[k][0] <= b[1]) b[1] = max(b[1], a[k++][1]);
        }
        res.push_back(b);
        //把在b区间右边和b没有交集的加入到答案里
        while (k < n) res.push_back(a[k++]);
        return res;
    }
};

 

352. 将数据流变为多个不相交区间

给一个数据输入流,把连续区间合并

关键点:找到左端点小于等于给定值的最后一个区间

typedef int LL;
#define F first
#define S second
class SummaryRanges {
public:
    set<pair<LL, LL>> s;
    /** Initialize your data structure here. */
    SummaryRanges() {
        s.insert({INT_MAX, INT_MAX});
        s.insert({INT_MIN, INT_MIN});
    }
    
    void addNum(int val) {
        //找到左端大于x的第一个区间
        auto j = s.upper_bound({val, INT_MAX});
        //左端点小于等于x最后一个区间
        auto i = j;
        i--;
        //区间已经包含了val
        if (i -> S >= val) return ;
        if (i -> S == val - 1 && j -> F == val + 1) {
            s.insert({i -> F, j -> S});
            s.erase(i);
            s.erase(j);
        } else if (i -> S == val - 1) {
            s.insert({i -> F, val});
            s.erase(i);
        } else if (j -> F == val + 1) {
            s.insert({val, j -> S});
            s.erase(j);
        } else {
            s.insert({val, val});
        }

    }
    
    vector<vector<int>> getIntervals() {
        vector<vector<int>> res;
        for (auto& x: s) {
            if (x.F != INT_MIN && x.F != INT_MAX) {
                res.push_back({x.F, x.S});
            }
        }
        return res;
    }
};

 

435. 无重叠区间

题目可以转化为可以最多选择多少不相交区间,按照右端点排序后,贪心选择即可

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){
            return a[1] < b[1];
        });
        int res = 0, n = intervals.size();
        int e = INT_MIN;
        for (int i = 0; i < n; i++) {
            int x = intervals[i][0], y = intervals[i][1];
            if (x >= e) {
                //没有重叠
                e = y;
                res++;
            }
        }
        return n - res;
    }
};

 

 

436. 寻找右区间

排序后二分即可。一个技巧,可以在vector内保存一个区间的下标,这样就不用定义结构体了

class Solution {
public:
    vector<int> findRightInterval(vector<vector<int>>& intervals) {
        int n = intervals.size();
        for (int i = 0; i < n; i++) intervals[i].push_back(i);
        sort(intervals.begin(), intervals.end());
        vector<int> res(n, -1);
        for (int i = 0; i < n; i++) {
            int x = intervals[i][0], y = intervals[i][1];
            int l = i, r = n - 1;
            while (l < r) {
                int mid = (l + r) / 2;
                if (intervals[mid][0] >= y) r = mid;
                else l = mid + 1;
            }
            if (intervals[l][0] >= y) res[intervals[i][2]] = intervals[l][2];
        }
        return res;
    }
};

 

452. 用最少数量的箭引爆气球

区间选点问题,等价于选择最多不相交的区间个数。按照右端点排序后贪心

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& a) {
        sort(a.begin(), a.end(), [](const vector<int>& x, const vector<int>& y){
            return x[1] < y[1];
        });
        int ans = 0, n = a.size();
        long long e = LONG_LONG_MIN;
        for (int i = 0; i < n; i++) {
            if (a[i][0] > e) {
                ans++;
                e = a[i][1];
            }
        }
        return ans;
    }
};

 

 

646. 最长数对链

等价于选择最多不相交区间

class Solution {
public:
    int findLongestChain(vector<vector<int>>& pairs) {
        sort(pairs.begin(), pairs.end(), [](vector<int>& a, vector<int>& b){
            return a[1] < b[1];
        });

        int res = 1, ed = pairs[0][1];
        for (auto& p: pairs) {
            if (p[0] > ed) {
                res ++ ;
                ed = p[1];
            }
        }

        return res;
    }
};

 

715. Range 模块

比较麻烦的一道题目

代码:https://github.com/dezhonger/LeetCode/blob/master/Leetcode0715.cpp

 

 

729. 我的日程安排表 I

判断一个区间和其他区间列表是否有交集

typedef pair<int, int> PII;
const int INF = 2e9;

class MyCalendar {
public:
    set<PII> S;

    MyCalendar() {
        S.insert({-INF, -INF});
        S.insert({INF, INF});
    }

    bool check(PII a, PII b) {
        if (a.second <= b.first || b.second <= a.first) return false;
        return true;
    }

    bool book(int start, int end) {
        auto i = S.lower_bound({start, -INF});
        auto j = i;
        j -- ;
        PII t(start, end);
        if (check(*i, t) || check(*j, t)) return false;
        S.insert(t);
        return true;
    }
};

 

 

731. 我的日程安排表 II

看似区间问题,实际可以转为差分来做

class MyCalendarTwo {
public:
    map<int, int> S;

    MyCalendarTwo() {

    }

    bool book(int start, int end) {
        S[start] ++ , S[end] -- ;
        int sum = 0;
        for (auto [k, v]: S) {
            sum += v;
            if (sum >= 3) {
                S[start] --, S[end] ++ ;
                return false;
            }
        }
        return true;
    }
};

 

732. 我的日程安排表 III

和上题完全一样

class MyCalendarThree {
public:
    map<int, int> S;

    MyCalendarThree() {

    }

    int book(int start, int end) {
        S[start] ++ , S[end] -- ;
        int sum = 0, res = 0;
        for (auto [k, v]: S) {
            sum += v;
            res = max(res, sum);
        }
        return res;
    }
};

 

757. 设置交集大小至少为2

区间选点问题,至少要有两个点

class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), [](vector<int>& a, vector<int>& b) {
            if (a[1] != b[1]) return a[1] < b[1];
            return a[0] > b[0];
        });
        vector<int> q(1, -1);
        int cnt = 0;
        for (auto& r: intervals) {
            if (r[0] > q[cnt]) {
                q.push_back(r[1] - 1);
                q.push_back(r[1]);
                cnt += 2;
            } else if (r[0] > q[cnt - 1]) {
                q.push_back(r[1]);
                cnt ++ ;
            }
        }
        return cnt;
    }
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值