LeetCode/设置交集大小至少为2(区间问题)

一个整数区间 [a, b]  ( a < b ) 代表着从 a 到 b 的所有连续整数,包括 a 和 b
给你一组整数区间intervals,请找到一个最小的集合 S,使得 S 里的元素与区间intervals中的每一个整数区间都至少有2个元素相交
输出这个最小集合S的大小

1. 交集大小至少为m(贪心法)

区间题目一般先进行排序
当按左边界进行升序排序,需要从后往前遍历,贪心选择左边界值
当按右边界进行升序排序,需要从前往后遍历,贪心选择右边界值
相等时,另一个边界降序排,右边界降序隔断右边影响,左边界降序隔断左边影响

//从后往前遍历,按左边界排序,贪心选择左边界的写法
class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int n = intervals.size();
        int res = 0;
        int m = 2;
        sort(intervals.begin(), intervals.end(), [&](vector<int>& a, vector<int>& b) {
            if (a[0] == b[0]) 
                return a[1] > b[1];//相同时右边界降序排(关键步骤),保证优先选小的,要选一起选
            return a[0] < b[0];//左边界升序排
        });
        vector<int> temp(n);//区域重叠数
        for (int i = n - 1; i >= 0; i--) {//从后往前遍历区间
            int left = intervals[i][0];//当前左边界值
            while(temp[i]++ < m) {
                res++;//贪心选择左边界作为元素插入
                for (int j = i-1; j >= 0; j--) {//从后往前遍历所有区间
                     if (intervals[j][1] < left) //前一个数右边界小于左边界
                        break;//中断查找
                temp[j]++;//否则刚才插入的左边界,同样满足前面区间的范围
                }
                left++;//选取左边界之后的数(因为要m个相交)
            }
        }
        return res;
    }
};
从前往后(按右边界排序)
class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int n = intervals.size();
        int res = 0;
        int m = 2;
        sort(intervals.begin(), intervals.end(), [&](vector<int>& a, vector<int>& b) {
            if (a[1] == b[1]) 
                return a[0] > b[0];//相同时左边界降序排(相同时要选一起选,否则下次再选)
            return a[1] < b[1];//右边界升序排
        });
        vector<int> temp(n);//区域重叠数
        for (int i = 0; i < n; i++) {//从前往后遍历区间
            int right = intervals[i][1];//当前右边界值
            while(temp[i]++ < m) {
                res++;//贪心选择右边界作为元素插入
                for (int j = i+1; j < n; j++) {//从后往前遍历所有区间
                     if (intervals[j][0] > right) //后一个数左边界大于右边界
                        break;//中断查找
                temp[j]++;//否则刚才插入的右边界,同样满足后面面区间的范围
                }
                right--;//选取右边界之前的数(因为要m个相交)
            }
        }
        return res;
    }
};

官方写法,从后往前
class Solution {
public:
    void help(vector<vector<int>>& intervals, vector<vector<int>>& temp, int pos, int num) {
        for (int i = pos; i >= 0; i--) {
            if (intervals[i][1] < num) {
                break;
            }
            temp[i].push_back(num);
        }
    }

    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int n = intervals.size();
        int res = 0;
        int m = 2;
        sort(intervals.begin(), intervals.end(), [&](vector<int>& a, vector<int>& b) {
            if (a[0] == b[0]) 
                return a[1] > b[1];
            return a[0] < b[0];//左边界升序排
        });
        vector<vector<int>> temp(n);
        for (int i = n - 1; i >= 0; i--) {
            for (int j = intervals[i][0], k = temp[i].size(); k < m; j++, k++) {
                res++;
                help(intervals, temp, i - 1, j);
            }
        }
        return res;
    }
};

2. 针对交集为2的点贪心(空间优化)

class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int n = intervals.size();
        int res = 0; int a,b = -1;
        sort(intervals.begin(), intervals.end(), [&](vector<int>& a, vector<int>& b) {
            return a[1] == b[1]?a[0] > b[0]:a[1]<b[1]; });
        for (int i = 0; i < n; i++) {//从前往后遍历区间
            int left = intervals[i][0],right = intervals[i][1];
            if(left>b){
                res = res + 2;//新增两个点
                a = right-1; b = right;//更新点值
            }
            else if(left>a){
                res++;//新增一个点
                a = b; b = right;
            }
        }
        return res;
    }
};
posted @ 2022-07-22 20:37  失控D大白兔  阅读(28)  评论(0编辑  收藏  举报